Hi,
after some thought, the changes into omp-low are not as obviously harmless as I
originally tought.  So i decided to handle this by separate patch.  This patch
simply makes cgraph to not release bodies of artificial functions that papers
around the problem in easier way.

Bootstrapped/regtested x86_64-linux, comitted.

Honza

        * cgraph.h (cgraph_remove_unreachable_nodes): Rename to ...
        (symtab_remove_unreachable_nodes): ... this one.
        * ipa-cp.c (ipcp_driver): Do not remove unreachable nodes.
        * cgraphunit.c (ipa_passes): Update.
        * cgraphclones.c (cgraph_materialize_all_clones): Update.
        * cgraph.c (cgraph_release_function_body): Only turn initial
        into error mark when initial was previously set.
        * ipa-inline.c (ipa_inline): Update.
        * ipa.c: Include ipa-inline.h
        (enqueue_cgraph_node, enqueue_varpool_node): Remove.
        (enqueue_node): New function.
        (process_references): Update.
        (symtab_remove_unreachable_nodes): Cleanup.
        * passes.c (execute_todo, execute_one_pass): Update.
Index: cgraph.c
===================================================================
*** cgraph.c    (revision 187335)
--- cgraph.c    (working copy)
*************** cgraph_release_function_body (struct cgr
*** 1162,1168 ****
    /* If the node is abstract and needed, then do not clear DECL_INITIAL
       of its associated function function declaration because it's
       needed to emit debug info later.  */
!   if (!node->abstract_and_needed)
      DECL_INITIAL (node->symbol.decl) = error_mark_node;
  }
  
--- 1162,1168 ----
    /* If the node is abstract and needed, then do not clear DECL_INITIAL
       of its associated function function declaration because it's
       needed to emit debug info later.  */
!   if (!node->abstract_and_needed && DECL_INITIAL (node->symbol.decl))
      DECL_INITIAL (node->symbol.decl) = error_mark_node;
  }
  
Index: cgraph.h
===================================================================
*** cgraph.h    (revision 187335)
--- cgraph.h    (working copy)
*************** int compute_call_stmt_bb_frequency (tree
*** 637,643 ****
  void record_references_in_initializer (tree, bool);
  
  /* In ipa.c  */
! bool cgraph_remove_unreachable_nodes (bool, FILE *);
  cgraph_node_set cgraph_node_set_new (void);
  cgraph_node_set_iterator cgraph_node_set_find (cgraph_node_set,
                                               struct cgraph_node *);
--- 637,643 ----
  void record_references_in_initializer (tree, bool);
  
  /* In ipa.c  */
! bool symtab_remove_unreachable_nodes (bool, FILE *);
  cgraph_node_set cgraph_node_set_new (void);
  cgraph_node_set_iterator cgraph_node_set_find (cgraph_node_set,
                                               struct cgraph_node *);
Index: ipa-cp.c
===================================================================
*** ipa-cp.c    (revision 187335)
--- ipa-cp.c    (working copy)
*************** ipcp_driver (void)
*** 2445,2451 ****
    struct cgraph_2edge_hook_list *edge_duplication_hook_holder;
    struct topo_info topo;
  
-   cgraph_remove_unreachable_nodes (true,dump_file);
    ipa_check_create_node_params ();
    ipa_check_create_edge_args ();
    grow_next_edge_clone_vector ();
--- 2445,2450 ----
Index: cgraphunit.c
===================================================================
*** cgraphunit.c        (revision 187335)
--- cgraphunit.c        (working copy)
*************** ipa_passes (void)
*** 1836,1842 ****
       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.  */
--- 1836,1842 ----
       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.  */
!   symtab_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.  */
*************** compile (void)
*** 1962,1968 ****
  
    /* This pass remove bodies of extern inline functions we never inlined.
       Do this later so other IPA passes see what is really going on.  */
!   cgraph_remove_unreachable_nodes (false, dump_file);
    cgraph_global_info_ready = true;
    if (cgraph_dump_file)
      {
--- 1962,1968 ----
  
    /* This pass remove bodies of extern inline functions we never inlined.
       Do this later so other IPA passes see what is really going on.  */
!   symtab_remove_unreachable_nodes (false, dump_file);
    cgraph_global_info_ready = true;
    if (cgraph_dump_file)
      {
*************** compile (void)
*** 1987,1993 ****
    cgraph_materialize_all_clones ();
    bitmap_obstack_initialize (NULL);
    execute_ipa_pass_list (all_late_ipa_passes);
!   cgraph_remove_unreachable_nodes (true, dump_file);
  #ifdef ENABLE_CHECKING
    verify_symtab ();
  #endif
--- 1987,1993 ----
    cgraph_materialize_all_clones ();
    bitmap_obstack_initialize (NULL);
    execute_ipa_pass_list (all_late_ipa_passes);
!   symtab_remove_unreachable_nodes (true, dump_file);
  #ifdef ENABLE_CHECKING
    verify_symtab ();
  #endif
Index: cgraphclones.c
===================================================================
*** cgraphclones.c      (revision 187335)
--- cgraphclones.c      (working copy)
*************** cgraph_materialize_all_clones (void)
*** 870,876 ****
  #ifdef ENABLE_CHECKING
    verify_cgraph ();
  #endif
!   cgraph_remove_unreachable_nodes (false, cgraph_dump_file);
  }
  
  #include "gt-cgraphclones.h"
--- 870,876 ----
  #ifdef ENABLE_CHECKING
    verify_cgraph ();
  #endif
!   symtab_remove_unreachable_nodes (false, cgraph_dump_file);
  }
  
  #include "gt-cgraphclones.h"
Index: ipa-inline.c
===================================================================
*** ipa-inline.c        (revision 187335)
--- ipa-inline.c        (working copy)
*************** ipa_inline (void)
*** 1717,1723 ****
      }
  
    inline_small_functions ();
!   cgraph_remove_unreachable_nodes (true, dump_file);
    free (order);
  
    /* We already perform some inlining of functions called once during
--- 1717,1723 ----
      }
  
    inline_small_functions ();
!   symtab_remove_unreachable_nodes (true, dump_file);
    free (order);
  
    /* We already perform some inlining of functions called once during
Index: ipa.c
===================================================================
*** ipa.c       (revision 187335)
--- ipa.c       (working copy)
*************** along with GCC; see the file COPYING3.  
*** 33,38 ****
--- 33,39 ----
  #include "tree-iterator.h"
  #include "ipa-utils.h"
  #include "pointer-set.h"
+ #include "ipa-inline.h"
  
  /* Look for all functions inlined to NODE and update their inlined_to pointers
     to INLINED_TO.  */
*************** update_inlined_to_pointer (struct cgraph
*** 49,55 ****
        }
  }
  
! /* Add cgraph NODE to queue starting at FIRST.
  
     The queue is linked via AUX pointers and terminated by pointer to 1.
     We enqueue nodes at two occasions: when we find them reachable or when we 
find
--- 50,56 ----
        }
  }
  
! /* Add symtab NODE to queue starting at FIRST.
  
     The queue is linked via AUX pointers and terminated by pointer to 1.
     We enqueue nodes at two occasions: when we find them reachable or when we 
find
*************** update_inlined_to_pointer (struct cgraph
*** 58,65 ****
     reachable.  */
  
  static void
! enqueue_cgraph_node (struct cgraph_node *node, struct cgraph_node **first,
!                    struct pointer_set_t *reachable)
  {
    /* Node is still in queue; do nothing.  */
    if (node->symbol.aux && node->symbol.aux != (void *) 2)
--- 59,66 ----
     reachable.  */
  
  static void
! enqueue_node (symtab_node node, symtab_node *first,
!             struct pointer_set_t *reachable)
  {
    /* Node is still in queue; do nothing.  */
    if (node->symbol.aux && node->symbol.aux != (void *) 2)
*************** enqueue_cgraph_node (struct cgraph_node 
*** 72,92 ****
    *first = node;
  }
  
- /* Add varpool NODE to queue starting at FIRST.  */
- 
- static void
- enqueue_varpool_node (struct varpool_node *node, struct varpool_node **first)
- {
-   node->symbol.aux = *first;
-   *first = node;
- }
- 
  /* Process references.  */
  
  static void
  process_references (struct ipa_ref_list *list,
!                   struct cgraph_node **first,
!                   struct varpool_node **first_varpool,
                    bool before_inlining_p,
                    struct pointer_set_t *reachable)
  {
--- 73,83 ----
    *first = node;
  }
  
  /* Process references.  */
  
  static void
  process_references (struct ipa_ref_list *list,
!                   symtab_node *first,
                    bool before_inlining_p,
                    struct pointer_set_t *reachable)
  {
*************** process_references (struct ipa_ref_list 
*** 97,114 ****
        if (symtab_function_p (ref->referred))
        {
          struct cgraph_node *node = ipa_ref_node (ref);
          if (node->analyzed
              && (!DECL_EXTERNAL (node->symbol.decl)
                  || node->alias
                  || before_inlining_p))
            pointer_set_insert (reachable, node);
!         enqueue_cgraph_node (node, first, reachable);
        }
        else
        {
          struct varpool_node *node = ipa_ref_varpool_node (ref);
!         if (!pointer_set_insert (reachable, node))
!           enqueue_varpool_node (node, first_varpool);
        }
      }
  }
--- 88,108 ----
        if (symtab_function_p (ref->referred))
        {
          struct cgraph_node *node = ipa_ref_node (ref);
+ 
          if (node->analyzed
              && (!DECL_EXTERNAL (node->symbol.decl)
                  || node->alias
                  || before_inlining_p))
            pointer_set_insert (reachable, node);
!         enqueue_node ((symtab_node) node, first, reachable);
        }
        else
        {
          struct varpool_node *node = ipa_ref_varpool_node (ref);
! 
!         if (node->analyzed)
!           pointer_set_insert (reachable, node);
!         enqueue_node ((symtab_node) node, first, reachable);
        }
      }
  }
*************** has_addr_references_p (struct cgraph_nod
*** 162,180 ****
  }
  
  /* Perform reachability analysis and reclaim all unreachable nodes.
!    If BEFORE_INLINING_P is true this function is called before inlining
!    decisions has been made.  If BEFORE_INLINING_P is false this function also
!    removes unneeded bodies of extern inline functions.  */
  
  bool
! cgraph_remove_unreachable_nodes (bool before_inlining_p, FILE *file)
  {
!   struct cgraph_node *first = (struct cgraph_node *) (void *) 1;
!   struct varpool_node *first_varpool = (struct varpool_node *) (void *) 1;
    struct cgraph_node *node, *next;
    struct varpool_node *vnode, *vnext;
    bool changed = false;
    struct pointer_set_t *reachable = pointer_set_create ();
  
  #ifdef ENABLE_CHECKING
    verify_symtab ();
--- 156,218 ----
  }
  
  /* Perform reachability analysis and reclaim all unreachable nodes.
! 
!    The algorithm is basically mark&sweep but with some extra refinements:
! 
!    - reachable extern inline functions needs special handling; the bodies 
needs
!      to stay in memory until inlining in hope that they will be inlined.
!      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. 
! 
!      Again we remove them after inlining.  In late optimization some
!      devirtualization may happen, but it is not importnat since we won't 
inline
!      the call. In theory early opts and IPA should work out all important 
cases.
! 
!    - virtual clones needs bodies of their origins for later materialization;
!      this means that we want to keep the body even if the origin is 
unreachable
!      otherwise.  To avoid origin from sitting in the callgraph and being
!      walked by IPA passes, we turn them into unanalyzed nodes with body
!      defined.
! 
!      We maintain set of function declaration where body needs to stay in
!      body_needed_for_clonning
! 
!      Inline clones represent special case: their declaration match the
!      declaration of origin and cgraph_remove_node already knows how to
!      reshape callgraph and preserve body when offline copy of function or
!      inline clone is being removed.
! 
!    We maintain queue of both reachable symbols (i.e. defined symbols that 
needs
!    to stay) and symbols that are in boundary (i.e. external symbols referenced
!    by reachable symbols or origins of clones).  The queue is represented
!    as linked list by AUX pointer terminated by 1.
! 
!    A the end we keep all reachable symbols. For symbols in boundary we always
!    turn definition into a declaration, but we may keep function body around
!    based on body_needed_for_clonning
! 
!    All symbols that enter the queue have AUX pointer non-zero and are in the
!    boundary.  Pointer set REACHABLE is used to track reachable symbols.
! 
!    Every symbol can be visited twice - once as part of boundary and once
!    as real reachable symbol. enqueue_node needs to decide whether the
!    node needs to be re-queued for second processing.  For this purpose
!    we set AUX pointer of processed symbols in the boundary to constant 2.  */
  
  bool
! symtab_remove_unreachable_nodes (bool before_inlining_p, FILE *file)
  {
!   symtab_node first = (symtab_node) (void *) 1;
    struct cgraph_node *node, *next;
    struct 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 ();
  
  #ifdef ENABLE_CHECKING
    verify_symtab ();
*************** cgraph_remove_unreachable_nodes (bool be
*** 191,198 ****
       This is mostly when they can be referenced externally.  Inline clones
       are special since their declarations are shared with master clone and 
thus
       cgraph_can_remove_if_no_direct_calls_and_refs_p should not be called on 
them.  */
!   FOR_EACH_FUNCTION (node)
!     if (node->analyzed && !node->global.inlined_to
        && (!cgraph_can_remove_if_no_direct_calls_and_refs_p (node)
            /* Keep around virtual functions for possible devirtualization.  */
            || (before_inlining_p
--- 229,236 ----
       This is mostly when they can be referenced externally.  Inline clones
       are special since their declarations are shared with master clone and 
thus
       cgraph_can_remove_if_no_direct_calls_and_refs_p should not be called on 
them.  */
!   FOR_EACH_DEFINED_FUNCTION (node)
!     if (!node->global.inlined_to
        && (!cgraph_can_remove_if_no_direct_calls_and_refs_p (node)
            /* Keep around virtual functions for possible devirtualization.  */
            || (before_inlining_p
*************** cgraph_remove_unreachable_nodes (bool be
*** 200,397 ****
                && (DECL_COMDAT (node->symbol.decl) || DECL_EXTERNAL 
(node->symbol.decl)))))
        {
          gcc_assert (!node->global.inlined_to);
-       enqueue_cgraph_node (node, &first, reachable);
        pointer_set_insert (reachable, node);
        }
      else
        gcc_assert (!node->symbol.aux);
  
    /* Mark variables that are obviously needed.  */
!   FOR_EACH_VARIABLE (vnode)
      {
!       if ((vnode->analyzed || vnode->symbol.force_output)
!         && !varpool_can_remove_if_no_refs (vnode))
!       {
!         pointer_set_insert (reachable, vnode);
!         enqueue_varpool_node (vnode, &first_varpool);
!       }
!     }
  
!   /* Perform reachability analysis.  As a special case do not consider
!      extern inline functions not inlined as live because we won't output
!      them at all. 
  
!      We maintain two worklist, one for cgraph nodes other for varpools and
!      are finished once both are empty.  */
  
!   while (first != (struct cgraph_node *) (void *) 1
!        || first_varpool != (struct varpool_node *) (void *) 1)
!     {
!       if (first != (struct cgraph_node *) (void *) 1)
        {
!         struct cgraph_edge *e;
!         node = first;
!         first = (struct cgraph_node *) first->symbol.aux;
!         if (!pointer_set_contains (reachable, node))
!           node->symbol.aux = (void *)2;
!         /* If we found this node reachable, first mark on the callees
!            reachable too, unless they are direct calls to extern inline 
functions
!            we decided to not inline.  */
!         else 
            {
!             for (e = node->callees; e; e = e->next_callee)
                {
!                 if (node->analyzed
                      && (!e->inline_failed
                          || !DECL_EXTERNAL (e->callee->symbol.decl)
!                         || node->alias
                          || before_inlining_p))
                    pointer_set_insert (reachable, e->callee);
!                 enqueue_cgraph_node (e->callee, &first, reachable);
!               }
!             process_references (&node->symbol.ref_list, &first,
!                                 &first_varpool, before_inlining_p,
!                                 reachable);
! 
!             /* If any function in a comdat group is reachable, force
!                all other functions in the same comdat group to be
!                also reachable.  */
!             if (node->symbol.same_comdat_group
!                 && !node->global.inlined_to)
!               {
!                 for (next = cgraph (node->symbol.same_comdat_group);
!                      next != node;
!                      next = cgraph (next->symbol.same_comdat_group))
!                   if (!pointer_set_insert (reachable, next))
!                     enqueue_cgraph_node (next, &first, reachable);
                }
            }
  
!         /* We can freely remove inline clones even if they are cloned, 
however if
!            function is clone of real clone, we must keep it around in order to
!            make materialize_clones produce function body with the changes
!            applied.  */
!         while (node->clone_of && !node->clone_of->symbol.aux
!                && !gimple_has_body_p (node->symbol.decl))
            {
!             bool noninline = node->clone_of->symbol.decl != node->symbol.decl;
!             node = node->clone_of;
!             if (noninline && !pointer_set_contains (reachable, node) && 
!node->symbol.aux)
                {
!                 enqueue_cgraph_node (node, &first, reachable);
                  break;
                }
            }
        }
-       if (first_varpool != (struct varpool_node *) (void *) 1)
-       {
-         vnode = first_varpool;
-         first_varpool = (struct varpool_node *)first_varpool->symbol.aux;
-         vnode->symbol.aux = NULL;
-         process_references (&vnode->symbol.ref_list, &first,
-                             &first_varpool, before_inlining_p,
-                             reachable);
-         /* If any function in a comdat group is reachable, force
-            all other functions in the same comdat group to be
-            also reachable.  */
-         if (vnode->symbol.same_comdat_group)
-           {
-             struct varpool_node *next;
-             for (next = varpool (vnode->symbol.same_comdat_group);
-                  next != vnode;
-                  next = varpool (next->symbol.same_comdat_group))
-               if (!pointer_set_insert (reachable, next))
-                 enqueue_varpool_node (next, &first_varpool);
-           }
-       }
      }
  
!   /* Remove unreachable nodes. 
! 
!      Completely unreachable functions can be fully removed from the callgraph.
!      Extern inline functions that we decided to not inline need to become 
unanalyzed nodes of
!      callgraph (so we still have edges to them).  We remove function body 
then.
! 
!      Also we need to care functions that are unreachable but we need to keep 
them around
!      for later clonning.  In this case we also turn them to unanalyzed nodes, 
but
!      keep the body around.  */
    for (node = cgraph_first_function (); node; node = next)
      {
        next = cgraph_next_function (node);
-       if (node->symbol.aux && !pointer_set_contains (reachable, node))
-         {
-         cgraph_node_remove_callees (node);
-         ipa_remove_all_references (&node->symbol.ref_list);
-         node->analyzed = false;
-       }
        if (!node->symbol.aux)
        {
-         struct cgraph_edge *e;
-         bool found = false;
-         int i;
-         struct ipa_ref *ref;
- 
-           node->global.inlined_to = NULL;
          if (file)
            fprintf (file, " %s", cgraph_node_name (node));
!         /* See if there is reachable caller.  */
!         for (e = node->callers; e && !found; e = e->next_caller)
!           if (pointer_set_contains (reachable, e->caller))
!             found = true;
!         for (i = 0; (ipa_ref_list_referring_iterate (&node->symbol.ref_list,
!                                                     i, ref)
!                      && !found); i++)
!           if (pointer_set_contains (reachable, ref->referring))
!             found = true;
! 
!         /* If so, we need to keep node in the callgraph.  */
!         if (found)
!           {
!             if (node->analyzed)
!               {
!                 struct cgraph_node *clone;
! 
!                 /* If there are still clones, we must keep body around.
!                    Otherwise we can just remove the body but keep the clone.  
*/
!                 for (clone = node->clones; clone;
!                      clone = clone->next_sibling_clone)
!                   if (clone->symbol.aux)
!                     break;
!                 if (!clone)
!                   {
!                     cgraph_release_function_body (node);
!                     if (node->prev_sibling_clone)
!                       node->prev_sibling_clone->next_sibling_clone = 
node->next_sibling_clone;
!                     else if (node->clone_of)
!                       node->clone_of->clones = node->next_sibling_clone;
!                     if (node->next_sibling_clone)
!                       node->next_sibling_clone->prev_sibling_clone = 
node->prev_sibling_clone;
!                     if (node->clone_of)
!                       node->former_clone_of = node->clone_of->symbol.decl;
!                     node->clone_of = NULL;
!                     node->next_sibling_clone = NULL;
!                     node->prev_sibling_clone = NULL;
!                   }
!                 else
!                   gcc_assert (!clone->symbol.in_other_partition);
!                 node->analyzed = false;
!                 changed = true;
!                 cgraph_node_remove_callees (node);
!                 ipa_remove_all_references (&node->symbol.ref_list);
!               }
!           }
!         else
            {
!             cgraph_remove_node (node);
              changed = true;
            }
        }
      }
    FOR_EACH_FUNCTION (node)
      {
-       /* Inline clones might be kept around so their materializing allows 
further
-          cloning.  If the function the clone is inlined into is removed, we 
need
-          to turn it into normal cone.  */
        if (node->global.inlined_to
          && !node->callers)
        {
--- 238,363 ----
                && (DECL_COMDAT (node->symbol.decl) || DECL_EXTERNAL 
(node->symbol.decl)))))
        {
          gcc_assert (!node->global.inlined_to);
        pointer_set_insert (reachable, node);
+       enqueue_node ((symtab_node)node, &first, reachable);
        }
      else
        gcc_assert (!node->symbol.aux);
  
    /* Mark variables that are obviously needed.  */
!   FOR_EACH_DEFINED_VARIABLE (vnode)
!     if (!varpool_can_remove_if_no_refs (vnode))
!       {
!       pointer_set_insert (reachable, vnode);
!       enqueue_node ((symtab_node)vnode, &first, reachable);
!       }
! 
!   /* Perform reachability analysis.  */
!   while (first != (symtab_node) (void *) 1)
      {
!       bool in_boundary_p = !pointer_set_contains (reachable, first);
!       symtab_node node = first;
  
!       first = (symtab_node)first->symbol.aux;
  
!       /* If we are processing symbol in boundary, mark its AUX pointer for
!        possible later re-processing in enqueue_node.  */
!       if (in_boundary_p)
!       node->symbol.aux = (void *)2;
!       else
!       {
!         /* If any symbol in a comdat group is reachable, force
!            all other in the same comdat group to be also reachable.  */
!         if (node->symbol.same_comdat_group)
!           {
!             symtab_node next;
!             for (next = node->symbol.same_comdat_group;
!                  next != node;
!                  next = next->symbol.same_comdat_group)
!               if (!pointer_set_insert (reachable, next))
!                 enqueue_node ((symtab_node) next, &first, reachable);
!           }
!         /* Mark references as reachable.  */
!         process_references (&node->symbol.ref_list, &first,
!                             before_inlining_p, reachable);
!       }
  
!       if (symtab_function_p (node))
        {
!         struct cgraph_node *cnode = cgraph (node);
! 
!         /* Mark the callees reachable unless they are direct calls to extern
!            inline functions we decided to not inline.  */
!         if (!in_boundary_p)
            {
!             struct cgraph_edge *e;
!             for (e = cnode->callees; e; e = e->next_callee)
                {
!                 if (e->callee->analyzed
                      && (!e->inline_failed
                          || !DECL_EXTERNAL (e->callee->symbol.decl)
!                         || cnode->alias
                          || before_inlining_p))
                    pointer_set_insert (reachable, e->callee);
!                 enqueue_node ((symtab_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->alias && cnode->global.inlined_to)
+               pointer_set_insert (body_needed_for_clonning, 
cnode->symbol.decl);
            }
  
!         /* For non-inline clones, force their origins to the boundary and 
ensure
!            that body is not removed.  */
!         while (cnode->clone_of && !cnode->clone_of->symbol.aux
!                && !gimple_has_body_p (cnode->symbol.decl))
            {
!             bool noninline = cnode->clone_of->symbol.decl != 
cnode->symbol.decl;
!             cnode = cnode->clone_of;
!             if (noninline && !cnode->symbol.aux)
                {
!                 pointer_set_insert (body_needed_for_clonning, 
cnode->symbol.decl);
!                 enqueue_node ((symtab_node)cnode, &first, reachable);
                  break;
                }
            }
        }
      }
  
!   /* Remove unreachable functions.   */
    for (node = cgraph_first_function (); node; node = next)
      {
        next = cgraph_next_function (node);
        if (!node->symbol.aux)
        {
          if (file)
            fprintf (file, " %s", cgraph_node_name (node));
!         cgraph_remove_node (node);
!         changed = true;
!       }
!       else if (!pointer_set_contains (reachable, node))
!         {
!         if (node->analyzed)
            {
!             if (file)
!               fprintf (file, " %s", cgraph_node_name (node));
!             cgraph_node_remove_callees (node);
!             ipa_remove_all_references (&node->symbol.ref_list);
              changed = true;
            }
+         if (!pointer_set_contains (body_needed_for_clonning, 
node->symbol.decl)
+             && !DECL_ARTIFICIAL (node->symbol.decl))
+           cgraph_release_function_body (node);
+         node->analyzed = false;
        }
      }
+ 
+   /* Inline clones might be kept around so their materializing allows further
+      cloning.  If the function the clone is inlined into is removed, we need
+      to turn it into normal cone.  */
    FOR_EACH_FUNCTION (node)
      {
        if (node->global.inlined_to
          && !node->callers)
        {
*************** cgraph_remove_unreachable_nodes (bool be
*** 402,426 ****
        node->symbol.aux = NULL;
      }
  
    if (file)
!     fprintf (file, "\n");
! 
!   if (file)
!     fprintf (file, "Reclaiming variables:");
    for (vnode = varpool_first_variable (); vnode; vnode = vnext)
      {
        vnext = varpool_next_variable (vnode);
!       if (!pointer_set_contains (reachable, vnode))
!         {
          if (file)
            fprintf (file, " %s", varpool_node_name (vnode));
          varpool_remove_node (vnode);
          changed = true;
        }
      }
  
!   /* Now update address_taken flags and try to promote functions to be local. 
 */
  
    if (file)
      fprintf (file, "\nClearing address taken flags:");
    FOR_EACH_DEFINED_FUNCTION (node)
--- 368,405 ----
        node->symbol.aux = NULL;
      }
  
+   /* Remove unreachable variables.  */
    if (file)
!     fprintf (file, "\nReclaiming variables:");
    for (vnode = varpool_first_variable (); vnode; vnode = vnext)
      {
        vnext = varpool_next_variable (vnode);
!       if (!vnode->symbol.aux)
!       {
          if (file)
            fprintf (file, " %s", varpool_node_name (vnode));
          varpool_remove_node (vnode);
          changed = true;
        }
+       else if (!pointer_set_contains (reachable, vnode))
+         {
+         if (vnode->analyzed)
+           {
+             if (file)
+               fprintf (file, " %s", varpool_node_name (vnode));
+             changed = true;
+           }
+         vnode->analyzed = false;
+         vnode->symbol.aux = NULL;
+       }
+       else
+       vnode->symbol.aux = NULL;
      }
  
!   pointer_set_destroy (reachable);
!   pointer_set_destroy (body_needed_for_clonning);
  
+   /* Now update address_taken flags and try to promote functions to be local. 
 */
    if (file)
      fprintf (file, "\nClearing address taken flags:");
    FOR_EACH_DEFINED_FUNCTION (node)
*************** cgraph_remove_unreachable_nodes (bool be
*** 444,461 ****
    if (file)
      fprintf (file, "\n");
  
-   /* Rest of transformations are undesirable at -O0.  */
-   if (!optimize)
-     return changed;
- 
  #ifdef ENABLE_CHECKING
    verify_symtab ();
  #endif
  
    /* Reclaim alias pairs for functions that have disappeared from the
       call graph.  */
    remove_unreachable_alias_pairs ();
-   pointer_set_destroy (reachable);
  
    return changed;
  }
--- 423,440 ----
    if (file)
      fprintf (file, "\n");
  
  #ifdef ENABLE_CHECKING
    verify_symtab ();
  #endif
  
+   /* If we removed something, perhaps profile could be improved.  */
+   if (changed && optimize && inline_edge_summary_vec)
+     FOR_EACH_DEFINED_FUNCTION (node)
+       cgraph_propagate_frequency (node);
+ 
    /* Reclaim alias pairs for functions that have disappeared from the
       call graph.  */
    remove_unreachable_alias_pairs ();
  
    return changed;
  }
Index: passes.c
===================================================================
*** passes.c    (revision 187335)
--- passes.c    (working copy)
*************** execute_todo (unsigned int flags)
*** 1865,1871 ****
    if (flags & TODO_remove_functions)
      {
        gcc_assert (!cfun);
!       cgraph_remove_unreachable_nodes (true, dump_file);
      }
  
    if ((flags & TODO_dump_symtab) && dump_file && !current_function_decl)
--- 1865,1871 ----
    if (flags & TODO_remove_functions)
      {
        gcc_assert (!cfun);
!       symtab_remove_unreachable_nodes (true, dump_file);
      }
  
    if ((flags & TODO_dump_symtab) && dump_file && !current_function_decl)
*************** execute_one_pass (struct opt_pass *pass)
*** 2150,2156 ****
        bool applied = false;
        do_per_function (apply_ipa_transforms, (void *)&applied);
        if (applied)
!         cgraph_remove_unreachable_nodes (true, dump_file);
        /* Restore current_pass.  */
        current_pass = pass;
      }
--- 2150,2156 ----
        bool applied = false;
        do_per_function (apply_ipa_transforms, (void *)&applied);
        if (applied)
!         symtab_remove_unreachable_nodes (true, dump_file);
        /* Restore current_pass.  */
        current_pass = pass;
      }

Reply via email to