diff mbox series

Free more of CFG in release_function_body

Message ID 20201124151605.GA56013@kam.mff.cuni.cz
State New
Headers show
Series Free more of CFG in release_function_body | expand

Commit Message

Jan Hubicka Nov. 24, 2020, 3:16 p.m. UTC
Hi,
at the end of processing function body we loop over basic blocks and
free all edges while we do not free the rest.  I think this is leftover
from time eges was not garbage collected and we was not using ggc_free.
It makes more sense to free all associated structures (which is
importnat for WPA memory footprint).

Bootstrapped/regtested x86_64-linux, OK?

Honza

	* cfg.c (free_block): New function.
	(clear_edges): Rename to ....
	(free_cfg): ... this one; also free BBs and vectors.
	(expunge_block): Update comment.
	* cfg.h (clear_edges): Rename to ...
	(free_cfg): ... this one.
	* cgraph.c (release_function_body): Use free_cfg.

Comments

Richard Biener Nov. 24, 2020, 3:50 p.m. UTC | #1
On Tue, 24 Nov 2020, Jan Hubicka wrote:

> Hi,
> at the end of processing function body we loop over basic blocks and
> free all edges while we do not free the rest.  I think this is leftover
> from time eges was not garbage collected and we was not using ggc_free.
> It makes more sense to free all associated structures (which is
> importnat for WPA memory footprint).
> 
> Bootstrapped/regtested x86_64-linux, OK?

OK.

> Honza
> 
> 	* cfg.c (free_block): New function.
> 	(clear_edges): Rename to ....
> 	(free_cfg): ... this one; also free BBs and vectors.
> 	(expunge_block): Update comment.
> 	* cfg.h (clear_edges): Rename to ...
> 	(free_cfg): ... this one.
> 	* cgraph.c (release_function_body): Use free_cfg.
> diff --git a/gcc/cfg.c b/gcc/cfg.c
> index de0e71db850..fc78e48d6e1 100644
> --- a/gcc/cfg.c
> +++ b/gcc/cfg.c
> @@ -25,7 +25,7 @@ along with GCC; see the file COPYING3.  If not see
>  
>     Available functionality:
>       - Initialization/deallocation
> -	 init_flow, clear_edges
> +	 init_flow, free_cfg
>       - Low level basic block manipulation
>  	 alloc_block, expunge_block
>       - Edge manipulation
> @@ -83,7 +83,7 @@ init_flow (struct function *the_fun)
>    the_fun->cfg->bb_flags_allocated = BB_ALL_FLAGS;
>  }
>  
> -/* Helper function for remove_edge and clear_edges.  Frees edge structure
> +/* Helper function for remove_edge and free_cffg.  Frees edge structure
>     without actually removing it from the pred/succ arrays.  */
>  
>  static void
> @@ -93,29 +93,41 @@ free_edge (function *fn, edge e)
>    ggc_free (e);
>  }
>  
> -/* Free the memory associated with the edge structures.  */
> +/* Free basic block BB.  */
> +
> +static void
> +free_block (basic_block bb)
> +{
> +   vec_free (bb->succs);
> +   vec_free (bb->preds);
> +   ggc_free (bb);
> +}
> +
> +/* Free the memory associated with the CFG in FN.  */
>  
>  void
> -clear_edges (struct function *fn)
> +free_cfg (struct function *fn)
>  {
> -  basic_block bb;
>    edge e;
>    edge_iterator ei;
> +  basic_block next;
>  
> -  FOR_EACH_BB_FN (bb, fn)
> +  for (basic_block bb = ENTRY_BLOCK_PTR_FOR_FN (fn); bb; bb = next)
>      {
> +      next = bb->next_bb;
>        FOR_EACH_EDGE (e, ei, bb->succs)
>  	free_edge (fn, e);
> -      vec_safe_truncate (bb->succs, 0);
> -      vec_safe_truncate (bb->preds, 0);
> +      free_block (bb);
>      }
>  
> -  FOR_EACH_EDGE (e, ei, ENTRY_BLOCK_PTR_FOR_FN (fn)->succs)
> -    free_edge (fn, e);
> -  vec_safe_truncate (EXIT_BLOCK_PTR_FOR_FN (fn)->preds, 0);
> -  vec_safe_truncate (ENTRY_BLOCK_PTR_FOR_FN (fn)->succs, 0);
> -
>    gcc_assert (!n_edges_for_fn (fn));
> +  /* Sanity check that dominance tree is freed.  */
> +  gcc_assert (!fn->cfg->x_dom_computed[0] && !fn->cfg->x_dom_computed[1]);
> +  
> +  vec_free (fn->cfg->x_label_to_block_map);
> +  vec_free (basic_block_info_for_fn (fn));
> +  ggc_free (fn->cfg);
> +  fn->cfg = NULL;
>  }
>  
>  /* Allocate memory for basic_block.  */
> @@ -190,8 +202,8 @@ expunge_block (basic_block b)
>    /* We should be able to ggc_free here, but we are not.
>       The dead SSA_NAMES are left pointing to dead statements that are pointing
>       to dead basic blocks making garbage collector to die.
> -     We should be able to release all dead SSA_NAMES and at the same time we should
> -     clear out BB pointer of dead statements consistently.  */
> +     We should be able to release all dead SSA_NAMES and at the same time we
> +     should clear out BB pointer of dead statements consistently.  */
>  }
>  
>  /* Connect E to E->src.  */
> diff --git a/gcc/cfg.h b/gcc/cfg.h
> index 93fde6df2bf..a9c8300f173 100644
> --- a/gcc/cfg.h
> +++ b/gcc/cfg.h
> @@ -82,7 +82,7 @@ struct GTY(()) control_flow_graph {
>  
>  
>  extern void init_flow (function *);
> -extern void clear_edges (function *);
> +extern void free_cfg (function *);
>  extern basic_block alloc_block (void);
>  extern void link_block (basic_block, basic_block);
>  extern void unlink_block (basic_block);
> diff --git a/gcc/cgraph.c b/gcc/cgraph.c
> index 19dfe2be23b..5c48a1bb92b 100644
> --- a/gcc/cgraph.c
> +++ b/gcc/cgraph.c
> @@ -1811,7 +1811,7 @@ release_function_body (tree decl)
>  	  gcc_assert (!dom_info_available_p (fn, CDI_DOMINATORS));
>  	  gcc_assert (!dom_info_available_p (fn, CDI_POST_DOMINATORS));
>  	  delete_tree_cfg_annotations (fn);
> -	  clear_edges (fn);
> +	  free_cfg (fn);
>  	  fn->cfg = NULL;
>  	}
>        if (fn->value_histograms)
>
Jan Hubicka Nov. 25, 2020, 2:08 p.m. UTC | #2
> On Tue, 24 Nov 2020, Jan Hubicka wrote:
> 
> > Hi,
> > at the end of processing function body we loop over basic blocks and
> > free all edges while we do not free the rest.  I think this is leftover
> > from time eges was not garbage collected and we was not using ggc_free.
> > It makes more sense to free all associated structures (which is
> > importnat for WPA memory footprint).
> > 
> > Bootstrapped/regtested x86_64-linux, OK?
> 
> OK.

Unforutnately the patch does not surive LTO bootstrap.  The problem is
that we keep DECL_INITIAL that points to blocks and blocks points to
var_decls and these points to SSA_NAMES that points to statements and
those points to basic blocks.

I wonder with early debug if we sitll need all the logic about keeping
DECL_INITIAL.

I have commited version that frees everything but the BB themselves and
will look into cleaning the pointers in decl_initial.

gcc/ChangeLog:

2020-11-25  Jan Hubicka  <hubicka@ucw.cz>

	* cfg.c (free_block): New function.
	(clear_edges): Rename to ....
	(free_cfg): ... this one; also free BBs and vectors.
	(expunge_block): Update comment.
	* cfg.h (clear_edges): Rename to ...
	(free_cfg): ... this one.
	* cgraph.c (release_function_body): Use free_cfg.

diff --git a/gcc/cfg.c b/gcc/cfg.c
index de0e71db850..529b6ed2105 100644
--- a/gcc/cfg.c
+++ b/gcc/cfg.c
@@ -25,7 +25,7 @@ along with GCC; see the file COPYING3.  If not see
 
    Available functionality:
      - Initialization/deallocation
-	 init_flow, clear_edges
+	 init_flow, free_cfg
      - Low level basic block manipulation
 	 alloc_block, expunge_block
      - Edge manipulation
@@ -83,7 +83,7 @@ init_flow (struct function *the_fun)
   the_fun->cfg->bb_flags_allocated = BB_ALL_FLAGS;
 }
 
-/* Helper function for remove_edge and clear_edges.  Frees edge structure
+/* Helper function for remove_edge and free_cffg.  Frees edge structure
    without actually removing it from the pred/succ arrays.  */
 
 static void
@@ -93,29 +93,44 @@ free_edge (function *fn, edge e)
   ggc_free (e);
 }
 
-/* Free the memory associated with the edge structures.  */
+/* Free basic block BB.  */
+
+static void
+free_block (basic_block bb)
+{
+   vec_free (bb->succs);
+   bb->succs = NULL;
+   vec_free (bb->preds);
+   bb->preds = NULL;
+   /* Do not free BB itself yet since we leak pointers to dead statements
+      that points to dead basic blocks.  */
+}
+
+/* Free the memory associated with the CFG in FN.  */
 
 void
-clear_edges (struct function *fn)
+free_cfg (struct function *fn)
 {
-  basic_block bb;
   edge e;
   edge_iterator ei;
+  basic_block next;
 
-  FOR_EACH_BB_FN (bb, fn)
+  for (basic_block bb = ENTRY_BLOCK_PTR_FOR_FN (fn); bb; bb = next)
     {
+      next = bb->next_bb;
       FOR_EACH_EDGE (e, ei, bb->succs)
 	free_edge (fn, e);
-      vec_safe_truncate (bb->succs, 0);
-      vec_safe_truncate (bb->preds, 0);
+      free_block (bb);
     }
 
-  FOR_EACH_EDGE (e, ei, ENTRY_BLOCK_PTR_FOR_FN (fn)->succs)
-    free_edge (fn, e);
-  vec_safe_truncate (EXIT_BLOCK_PTR_FOR_FN (fn)->preds, 0);
-  vec_safe_truncate (ENTRY_BLOCK_PTR_FOR_FN (fn)->succs, 0);
-
   gcc_assert (!n_edges_for_fn (fn));
+  /* Sanity check that dominance tree is freed.  */
+  gcc_assert (!fn->cfg->x_dom_computed[0] && !fn->cfg->x_dom_computed[1]);
+  
+  vec_free (fn->cfg->x_label_to_block_map);
+  vec_free (basic_block_info_for_fn (fn));
+  ggc_free (fn->cfg);
+  fn->cfg = NULL;
 }
 
 /* Allocate memory for basic_block.  */
@@ -190,8 +205,8 @@ expunge_block (basic_block b)
   /* We should be able to ggc_free here, but we are not.
      The dead SSA_NAMES are left pointing to dead statements that are pointing
      to dead basic blocks making garbage collector to die.
-     We should be able to release all dead SSA_NAMES and at the same time we should
-     clear out BB pointer of dead statements consistently.  */
+     We should be able to release all dead SSA_NAMES and at the same time we
+     should clear out BB pointer of dead statements consistently.  */
 }
 
 /* Connect E to E->src.  */
diff --git a/gcc/cfg.h b/gcc/cfg.h
index 93fde6df2bf..a9c8300f173 100644
--- a/gcc/cfg.h
+++ b/gcc/cfg.h
@@ -82,7 +82,7 @@ struct GTY(()) control_flow_graph {
 
 
 extern void init_flow (function *);
-extern void clear_edges (function *);
+extern void free_cfg (function *);
 extern basic_block alloc_block (void);
 extern void link_block (basic_block, basic_block);
 extern void unlink_block (basic_block);
diff --git a/gcc/cgraph.c b/gcc/cgraph.c
index 19dfe2be23b..dbde8aaaba1 100644
--- a/gcc/cgraph.c
+++ b/gcc/cgraph.c
@@ -1811,7 +1811,7 @@ release_function_body (tree decl)
 	  gcc_assert (!dom_info_available_p (fn, CDI_DOMINATORS));
 	  gcc_assert (!dom_info_available_p (fn, CDI_POST_DOMINATORS));
 	  delete_tree_cfg_annotations (fn);
-	  clear_edges (fn);
+	  free_cfg (fn);
 	  fn->cfg = NULL;
 	}
       if (fn->value_histograms)
diff --git a/gcc/tree-ssa-structalias.c b/gcc/tree-ssa-structalias.c
index a4832b75436..5f84f7d467f 100644
--- a/gcc/tree-ssa-structalias.c
+++ b/gcc/tree-ssa-structalias.c
@@ -4253,12 +4253,20 @@ handle_pure_call (gcall *stmt, vec<ce_s> *results)
   for (i = 0; i < gimple_call_num_args (stmt); ++i)
     {
       tree arg = gimple_call_arg (stmt, i);
+      int flags = gimple_call_arg_flags (stmt, i);
+
+      if (flags & EAF_UNUSED)
+	continue;
+
       if (!uses)
-	{
-	  uses = get_call_use_vi (stmt);
-	  make_any_offset_constraints (uses);
-	  make_transitive_closure_constraints (uses);
-	}
+	uses = get_call_use_vi (stmt);
+      varinfo_t tem = new_var_info (NULL_TREE, "callarg", true);
+      tem->is_reg_var = true;
+      make_constraint_to (tem->id, arg);
+      make_any_offset_constraints (tem);
+      if (!(flags & EAF_DIRECT))
+	make_transitive_closure_constraints (tem);
+      make_copy_constraint (uses, tem->id);
       make_constraint_to (uses->id, arg);
     }
Richard Biener Nov. 25, 2020, 2:28 p.m. UTC | #3
On Wed, Nov 25, 2020 at 3:11 PM Jan Hubicka <hubicka@ucw.cz> wrote:
>
> > On Tue, 24 Nov 2020, Jan Hubicka wrote:
> >
> > > Hi,
> > > at the end of processing function body we loop over basic blocks and
> > > free all edges while we do not free the rest.  I think this is leftover
> > > from time eges was not garbage collected and we was not using ggc_free.
> > > It makes more sense to free all associated structures (which is
> > > importnat for WPA memory footprint).
> > >
> > > Bootstrapped/regtested x86_64-linux, OK?
> >
> > OK.
>
> Unforutnately the patch does not surive LTO bootstrap.  The problem is
> that we keep DECL_INITIAL that points to blocks and blocks points to
> var_decls and these points to SSA_NAMES that points to statements and
> those points to basic blocks.

VAR_DECLs point to SSA_NAMEs?  It's the other way around.  We for sure
free SSA_NAMEs (well, maybe not explicitely with ggc_free).

Richard.

>
> I wonder with early debug if we sitll need all the logic about keeping
> DECL_INITIAL.
>
> I have commited version that frees everything but the BB themselves and
> will look into cleaning the pointers in decl_initial.
>
> gcc/ChangeLog:
>
> 2020-11-25  Jan Hubicka  <hubicka@ucw.cz>
>
>         * cfg.c (free_block): New function.
>         (clear_edges): Rename to ....
>         (free_cfg): ... this one; also free BBs and vectors.
>         (expunge_block): Update comment.
>         * cfg.h (clear_edges): Rename to ...
>         (free_cfg): ... this one.
>         * cgraph.c (release_function_body): Use free_cfg.
>
> diff --git a/gcc/cfg.c b/gcc/cfg.c
> index de0e71db850..529b6ed2105 100644
> --- a/gcc/cfg.c
> +++ b/gcc/cfg.c
> @@ -25,7 +25,7 @@ along with GCC; see the file COPYING3.  If not see
>
>     Available functionality:
>       - Initialization/deallocation
> -        init_flow, clear_edges
> +        init_flow, free_cfg
>       - Low level basic block manipulation
>          alloc_block, expunge_block
>       - Edge manipulation
> @@ -83,7 +83,7 @@ init_flow (struct function *the_fun)
>    the_fun->cfg->bb_flags_allocated = BB_ALL_FLAGS;
>  }
>
> -/* Helper function for remove_edge and clear_edges.  Frees edge structure
> +/* Helper function for remove_edge and free_cffg.  Frees edge structure
>     without actually removing it from the pred/succ arrays.  */
>
>  static void
> @@ -93,29 +93,44 @@ free_edge (function *fn, edge e)
>    ggc_free (e);
>  }
>
> -/* Free the memory associated with the edge structures.  */
> +/* Free basic block BB.  */
> +
> +static void
> +free_block (basic_block bb)
> +{
> +   vec_free (bb->succs);
> +   bb->succs = NULL;
> +   vec_free (bb->preds);
> +   bb->preds = NULL;
> +   /* Do not free BB itself yet since we leak pointers to dead statements
> +      that points to dead basic blocks.  */
> +}
> +
> +/* Free the memory associated with the CFG in FN.  */
>
>  void
> -clear_edges (struct function *fn)
> +free_cfg (struct function *fn)
>  {
> -  basic_block bb;
>    edge e;
>    edge_iterator ei;
> +  basic_block next;
>
> -  FOR_EACH_BB_FN (bb, fn)
> +  for (basic_block bb = ENTRY_BLOCK_PTR_FOR_FN (fn); bb; bb = next)
>      {
> +      next = bb->next_bb;
>        FOR_EACH_EDGE (e, ei, bb->succs)
>         free_edge (fn, e);
> -      vec_safe_truncate (bb->succs, 0);
> -      vec_safe_truncate (bb->preds, 0);
> +      free_block (bb);
>      }
>
> -  FOR_EACH_EDGE (e, ei, ENTRY_BLOCK_PTR_FOR_FN (fn)->succs)
> -    free_edge (fn, e);
> -  vec_safe_truncate (EXIT_BLOCK_PTR_FOR_FN (fn)->preds, 0);
> -  vec_safe_truncate (ENTRY_BLOCK_PTR_FOR_FN (fn)->succs, 0);
> -
>    gcc_assert (!n_edges_for_fn (fn));
> +  /* Sanity check that dominance tree is freed.  */
> +  gcc_assert (!fn->cfg->x_dom_computed[0] && !fn->cfg->x_dom_computed[1]);
> +
> +  vec_free (fn->cfg->x_label_to_block_map);
> +  vec_free (basic_block_info_for_fn (fn));
> +  ggc_free (fn->cfg);
> +  fn->cfg = NULL;
>  }
>
>  /* Allocate memory for basic_block.  */
> @@ -190,8 +205,8 @@ expunge_block (basic_block b)
>    /* We should be able to ggc_free here, but we are not.
>       The dead SSA_NAMES are left pointing to dead statements that are pointing
>       to dead basic blocks making garbage collector to die.
> -     We should be able to release all dead SSA_NAMES and at the same time we should
> -     clear out BB pointer of dead statements consistently.  */
> +     We should be able to release all dead SSA_NAMES and at the same time we
> +     should clear out BB pointer of dead statements consistently.  */
>  }
>
>  /* Connect E to E->src.  */
> diff --git a/gcc/cfg.h b/gcc/cfg.h
> index 93fde6df2bf..a9c8300f173 100644
> --- a/gcc/cfg.h
> +++ b/gcc/cfg.h
> @@ -82,7 +82,7 @@ struct GTY(()) control_flow_graph {
>
>
>  extern void init_flow (function *);
> -extern void clear_edges (function *);
> +extern void free_cfg (function *);
>  extern basic_block alloc_block (void);
>  extern void link_block (basic_block, basic_block);
>  extern void unlink_block (basic_block);
> diff --git a/gcc/cgraph.c b/gcc/cgraph.c
> index 19dfe2be23b..dbde8aaaba1 100644
> --- a/gcc/cgraph.c
> +++ b/gcc/cgraph.c
> @@ -1811,7 +1811,7 @@ release_function_body (tree decl)
>           gcc_assert (!dom_info_available_p (fn, CDI_DOMINATORS));
>           gcc_assert (!dom_info_available_p (fn, CDI_POST_DOMINATORS));
>           delete_tree_cfg_annotations (fn);
> -         clear_edges (fn);
> +         free_cfg (fn);
>           fn->cfg = NULL;
>         }
>        if (fn->value_histograms)
> diff --git a/gcc/tree-ssa-structalias.c b/gcc/tree-ssa-structalias.c
> index a4832b75436..5f84f7d467f 100644
> --- a/gcc/tree-ssa-structalias.c
> +++ b/gcc/tree-ssa-structalias.c
> @@ -4253,12 +4253,20 @@ handle_pure_call (gcall *stmt, vec<ce_s> *results)
>    for (i = 0; i < gimple_call_num_args (stmt); ++i)
>      {
>        tree arg = gimple_call_arg (stmt, i);
> +      int flags = gimple_call_arg_flags (stmt, i);
> +
> +      if (flags & EAF_UNUSED)
> +       continue;
> +
>        if (!uses)
> -       {
> -         uses = get_call_use_vi (stmt);
> -         make_any_offset_constraints (uses);
> -         make_transitive_closure_constraints (uses);
> -       }
> +       uses = get_call_use_vi (stmt);
> +      varinfo_t tem = new_var_info (NULL_TREE, "callarg", true);
> +      tem->is_reg_var = true;
> +      make_constraint_to (tem->id, arg);
> +      make_any_offset_constraints (tem);
> +      if (!(flags & EAF_DIRECT))
> +       make_transitive_closure_constraints (tem);
> +      make_copy_constraint (uses, tem->id);
>        make_constraint_to (uses->id, arg);
>      }
>
Jan Hubicka Nov. 25, 2020, 2:30 p.m. UTC | #4
> On Wed, Nov 25, 2020 at 3:11 PM Jan Hubicka <hubicka@ucw.cz> wrote:
> >
> > > On Tue, 24 Nov 2020, Jan Hubicka wrote:
> > >
> > > > Hi,
> > > > at the end of processing function body we loop over basic blocks and
> > > > free all edges while we do not free the rest.  I think this is leftover
> > > > from time eges was not garbage collected and we was not using ggc_free.
> > > > It makes more sense to free all associated structures (which is
> > > > importnat for WPA memory footprint).
> > > >
> > > > Bootstrapped/regtested x86_64-linux, OK?
> > >
> > > OK.
> >
> > Unforutnately the patch does not surive LTO bootstrap.  The problem is
> > that we keep DECL_INITIAL that points to blocks and blocks points to
> > var_decls and these points to SSA_NAMES that points to statements and
> > those points to basic blocks.
> 
> VAR_DECLs point to SSA_NAMEs?  It's the other way around.  We for sure
> free SSA_NAMEs (well, maybe not explicitely with ggc_free).

I am going to debug this more carefully now.  I think it was VAR_DECL
with variadic type pointing to SSA_NAME.  Should be easy to reduct with
gcac compiler.

Honza
Richard Biener Nov. 25, 2020, 2:32 p.m. UTC | #5
On Wed, 25 Nov 2020, Jan Hubicka wrote:

> > On Wed, Nov 25, 2020 at 3:11 PM Jan Hubicka <hubicka@ucw.cz> wrote:
> > >
> > > > On Tue, 24 Nov 2020, Jan Hubicka wrote:
> > > >
> > > > > Hi,
> > > > > at the end of processing function body we loop over basic blocks and
> > > > > free all edges while we do not free the rest.  I think this is leftover
> > > > > from time eges was not garbage collected and we was not using ggc_free.
> > > > > It makes more sense to free all associated structures (which is
> > > > > importnat for WPA memory footprint).
> > > > >
> > > > > Bootstrapped/regtested x86_64-linux, OK?
> > > >
> > > > OK.
> > >
> > > Unforutnately the patch does not surive LTO bootstrap.  The problem is
> > > that we keep DECL_INITIAL that points to blocks and blocks points to
> > > var_decls and these points to SSA_NAMES that points to statements and
> > > those points to basic blocks.
> > 
> > VAR_DECLs point to SSA_NAMEs?  It's the other way around.  We for sure
> > free SSA_NAMEs (well, maybe not explicitely with ggc_free).
> 
> I am going to debug this more carefully now.  I think it was VAR_DECL
> with variadic type pointing to SSA_NAME.  Should be easy to reduct with
> gcac compiler.

Possibly another case of a missing DECL_EXPR then.  In those cases
SSA names leak into TYPE_MIN/MAX_VALUE or TYPE/DECL_SIZE during
gimplification.  But those are frontend bugs (there are plenty reported).

Richard.
diff mbox series

Patch

diff --git a/gcc/cfg.c b/gcc/cfg.c
index de0e71db850..fc78e48d6e1 100644
--- a/gcc/cfg.c
+++ b/gcc/cfg.c
@@ -25,7 +25,7 @@  along with GCC; see the file COPYING3.  If not see
 
    Available functionality:
      - Initialization/deallocation
-	 init_flow, clear_edges
+	 init_flow, free_cfg
      - Low level basic block manipulation
 	 alloc_block, expunge_block
      - Edge manipulation
@@ -83,7 +83,7 @@  init_flow (struct function *the_fun)
   the_fun->cfg->bb_flags_allocated = BB_ALL_FLAGS;
 }
 
-/* Helper function for remove_edge and clear_edges.  Frees edge structure
+/* Helper function for remove_edge and free_cffg.  Frees edge structure
    without actually removing it from the pred/succ arrays.  */
 
 static void
@@ -93,29 +93,41 @@  free_edge (function *fn, edge e)
   ggc_free (e);
 }
 
-/* Free the memory associated with the edge structures.  */
+/* Free basic block BB.  */
+
+static void
+free_block (basic_block bb)
+{
+   vec_free (bb->succs);
+   vec_free (bb->preds);
+   ggc_free (bb);
+}
+
+/* Free the memory associated with the CFG in FN.  */
 
 void
-clear_edges (struct function *fn)
+free_cfg (struct function *fn)
 {
-  basic_block bb;
   edge e;
   edge_iterator ei;
+  basic_block next;
 
-  FOR_EACH_BB_FN (bb, fn)
+  for (basic_block bb = ENTRY_BLOCK_PTR_FOR_FN (fn); bb; bb = next)
     {
+      next = bb->next_bb;
       FOR_EACH_EDGE (e, ei, bb->succs)
 	free_edge (fn, e);
-      vec_safe_truncate (bb->succs, 0);
-      vec_safe_truncate (bb->preds, 0);
+      free_block (bb);
     }
 
-  FOR_EACH_EDGE (e, ei, ENTRY_BLOCK_PTR_FOR_FN (fn)->succs)
-    free_edge (fn, e);
-  vec_safe_truncate (EXIT_BLOCK_PTR_FOR_FN (fn)->preds, 0);
-  vec_safe_truncate (ENTRY_BLOCK_PTR_FOR_FN (fn)->succs, 0);
-
   gcc_assert (!n_edges_for_fn (fn));
+  /* Sanity check that dominance tree is freed.  */
+  gcc_assert (!fn->cfg->x_dom_computed[0] && !fn->cfg->x_dom_computed[1]);
+  
+  vec_free (fn->cfg->x_label_to_block_map);
+  vec_free (basic_block_info_for_fn (fn));
+  ggc_free (fn->cfg);
+  fn->cfg = NULL;
 }
 
 /* Allocate memory for basic_block.  */
@@ -190,8 +202,8 @@  expunge_block (basic_block b)
   /* We should be able to ggc_free here, but we are not.
      The dead SSA_NAMES are left pointing to dead statements that are pointing
      to dead basic blocks making garbage collector to die.
-     We should be able to release all dead SSA_NAMES and at the same time we should
-     clear out BB pointer of dead statements consistently.  */
+     We should be able to release all dead SSA_NAMES and at the same time we
+     should clear out BB pointer of dead statements consistently.  */
 }
 
 /* Connect E to E->src.  */
diff --git a/gcc/cfg.h b/gcc/cfg.h
index 93fde6df2bf..a9c8300f173 100644
--- a/gcc/cfg.h
+++ b/gcc/cfg.h
@@ -82,7 +82,7 @@  struct GTY(()) control_flow_graph {
 
 
 extern void init_flow (function *);
-extern void clear_edges (function *);
+extern void free_cfg (function *);
 extern basic_block alloc_block (void);
 extern void link_block (basic_block, basic_block);
 extern void unlink_block (basic_block);
diff --git a/gcc/cgraph.c b/gcc/cgraph.c
index 19dfe2be23b..5c48a1bb92b 100644
--- a/gcc/cgraph.c
+++ b/gcc/cgraph.c
@@ -1811,7 +1811,7 @@  release_function_body (tree decl)
 	  gcc_assert (!dom_info_available_p (fn, CDI_DOMINATORS));
 	  gcc_assert (!dom_info_available_p (fn, CDI_POST_DOMINATORS));
 	  delete_tree_cfg_annotations (fn);
-	  clear_edges (fn);
+	  free_cfg (fn);
 	  fn->cfg = NULL;
 	}
       if (fn->value_histograms)