Symbol table 20/many: cleanup of cgraph_remove_unreachable_nodes

Jan Hubicka hubicka@ucw.cz
Thu May 10 20:20:00 GMT 2012


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;
      }



More information about the Gcc-patches mailing list