diff mbox

Fix memory leak in Fortran FE with init_emit (take 2)

Message ID 20120823074548.GW1999@tucnak.redhat.com
State New
Headers show

Commit Message

Jakub Jelinek Aug. 23, 2012, 7:45 a.m. UTC
On Wed, Aug 22, 2012 at 03:29:28PM +0200, Richard Guenther wrote:
> On Wed, Aug 22, 2012 at 3:03 PM, Jakub Jelinek <jakub@redhat.com> wrote:
> > init_function_start is these days supposed to be called only at the start of
> > RTL expansion, it shouldn't be called much earlier, because then we leak
> > e.g. the memory allocated by init_emit.
> >
> > Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?
> 
> Looks ok, but please move the assert into allocate_struct_function instead.

I've done that, but unfortunately that first revealed in a problem that
many functions would do copy_node followed by tree_function_versioning which
called in the end allocate_struct_function (solved after IRC discussion
by tweaking copy_node).  Another issue is in the C++ FE, where
g++.dg/ext/gnu-inline-template-func.C testcase failed.  Hacked around
by not calling allocate_struct_function in that case, but am not 100% sure
what exactly we should be doing in that case and why it only triggers during
template processing - first start_preparsed_function is called with one decl
(the GNU extern inline decl), next it is called with another decl, but
  /* We must call push_template_decl after current_class_type is set
     up.  (If we are processing inline definitions after exiting a
     class scope, current_class_type will be NULL_TREE until set above
     by push_nested_class.)  */
  if (processing_template_decl)
    {
      /* FIXME: Handle error_mark_node more gracefully.  */
      tree newdecl1 = push_template_decl (decl1);
      if (newdecl1 != error_mark_node)
        decl1 = newdecl1;
    }
sets decl1 to the GNU extern inline decl on which start_preparsed_function
has been already called.  Jason, any ideas?

Perhaps it would make sense to commit the fortran/ change separately to fix
the leak in there, and resolve the rest in a follow-up when it is clear what
to do in the C++ FE.

2012-08-23  Jakub Jelinek  <jakub@redhat.com>

	* function.c (allocate_struct_function): Assert that
	DECL_STRUCT_FUNCTION (fndecl) is NULL.
	* tree.c (copy_node_stat): Clear DECL_STRUCT_FUNCTION.
fortran/
	* trans-decl.c (trans_function_start, generate_coarray_init,
	create_main_function, gfc_generate_constructors): Call
	allocate_struct_function instead of init_function_start.
cp/
	* decl.c (start_preparsed_function): Don't call
	allocate_struct_function if already allocated.


	Jakub

Comments

Richard Biener Aug. 23, 2012, 9:24 a.m. UTC | #1
On Thu, Aug 23, 2012 at 9:45 AM, Jakub Jelinek <jakub@redhat.com> wrote:
> On Wed, Aug 22, 2012 at 03:29:28PM +0200, Richard Guenther wrote:
>> On Wed, Aug 22, 2012 at 3:03 PM, Jakub Jelinek <jakub@redhat.com> wrote:
>> > init_function_start is these days supposed to be called only at the start of
>> > RTL expansion, it shouldn't be called much earlier, because then we leak
>> > e.g. the memory allocated by init_emit.
>> >
>> > Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?
>>
>> Looks ok, but please move the assert into allocate_struct_function instead.
>
> I've done that, but unfortunately that first revealed in a problem that
> many functions would do copy_node followed by tree_function_versioning which
> called in the end allocate_struct_function (solved after IRC discussion
> by tweaking copy_node).  Another issue is in the C++ FE, where
> g++.dg/ext/gnu-inline-template-func.C testcase failed.  Hacked around
> by not calling allocate_struct_function in that case, but am not 100% sure
> what exactly we should be doing in that case and why it only triggers during
> template processing - first start_preparsed_function is called with one decl
> (the GNU extern inline decl), next it is called with another decl, but
>   /* We must call push_template_decl after current_class_type is set
>      up.  (If we are processing inline definitions after exiting a
>      class scope, current_class_type will be NULL_TREE until set above
>      by push_nested_class.)  */
>   if (processing_template_decl)
>     {
>       /* FIXME: Handle error_mark_node more gracefully.  */
>       tree newdecl1 = push_template_decl (decl1);
>       if (newdecl1 != error_mark_node)
>         decl1 = newdecl1;
>     }
> sets decl1 to the GNU extern inline decl on which start_preparsed_function
> has been already called.  Jason, any ideas?
>
> Perhaps it would make sense to commit the fortran/ change separately to fix
> the leak in there, and resolve the rest in a follow-up when it is clear what
> to do in the C++ FE.

Yes.  Does the copy_node change pass testing?  If so then installing it
would be nice as well.

Thanks,
Richard.

> 2012-08-23  Jakub Jelinek  <jakub@redhat.com>
>
>         * function.c (allocate_struct_function): Assert that
>         DECL_STRUCT_FUNCTION (fndecl) is NULL.
>         * tree.c (copy_node_stat): Clear DECL_STRUCT_FUNCTION.
> fortran/
>         * trans-decl.c (trans_function_start, generate_coarray_init,
>         create_main_function, gfc_generate_constructors): Call
>         allocate_struct_function instead of init_function_start.
> cp/
>         * decl.c (start_preparsed_function): Don't call
>         allocate_struct_function if already allocated.
>
> --- gcc/cp/decl.c.jj    2012-08-22 00:12:55.000000000 +0200
> +++ gcc/cp/decl.c       2012-08-22 22:35:24.040391935 +0200
> @@ -12882,7 +12882,10 @@ start_preparsed_function (tree decl1, tr
>       CFUN set up, and our per-function variables initialized.
>       FIXME factor out the non-RTL stuff.  */
>    bl = current_binding_level;
> -  allocate_struct_function (decl1, processing_template_decl);
> +  if (DECL_STRUCT_FUNCTION (decl1) == NULL)
> +    allocate_struct_function (decl1, processing_template_decl);
> +  else
> +    set_cfun (DECL_STRUCT_FUNCTION (decl1));
>
>    /* Initialize the language data structures.  Whenever we start
>       a new function, we destroy temporaries in the usual way.  */
> --- gcc/function.c.jj   2012-08-22 22:16:07.199445894 +0200
> +++ gcc/function.c      2012-08-22 22:37:15.449893843 +0200
> @@ -4479,6 +4479,7 @@ allocate_struct_function (tree fndecl, b
>
>    if (fndecl != NULL_TREE)
>      {
> +      gcc_checking_assert (DECL_STRUCT_FUNCTION (fndecl) == NULL);
>        DECL_STRUCT_FUNCTION (fndecl) = cfun;
>        cfun->decl = fndecl;
>        current_function_funcdef_no = get_next_funcdef_no ();
> --- gcc/tree.c.jj       2012-08-22 22:16:07.175445954 +0200
> +++ gcc/tree.c  2012-08-22 22:37:15.451893834 +0200
> @@ -983,6 +983,8 @@ copy_node_stat (tree node MEM_STAT_DECL)
>           SET_DECL_INIT_PRIORITY (t, DECL_INIT_PRIORITY (node));
>           DECL_HAS_INIT_PRIORITY_P (t) = 1;
>         }
> +      if (TREE_CODE (node) == FUNCTION_DECL)
> +       DECL_STRUCT_FUNCTION (t) = NULL;
>      }
>    else if (TREE_CODE_CLASS (code) == tcc_type)
>      {
> --- gcc/fortran/trans-decl.c.jj 2012-08-22 14:55:21.358138432 +0200
> +++ gcc/fortran/trans-decl.c    2012-08-22 22:37:15.476893717 +0200
> @@ -2265,7 +2265,7 @@ trans_function_start (gfc_symbol * sym)
>    /* Create RTL for function definition.  */
>    make_decl_rtl (fndecl);
>
> -  init_function_start (fndecl);
> +  allocate_struct_function (fndecl, false);
>
>    /* function.c requires a push at the start of the function.  */
>    pushlevel ();
> @@ -4408,7 +4408,7 @@ generate_coarray_init (gfc_namespace * n
>
>    rest_of_decl_compilation (fndecl, 0, 0);
>    make_decl_rtl (fndecl);
> -  init_function_start (fndecl);
> +  allocate_struct_function (fndecl, false);
>
>    pushlevel ();
>    gfc_init_block (&caf_init_block);
> @@ -4982,7 +4982,7 @@ create_main_function (tree fndecl)
>
>    rest_of_decl_compilation (ftn_main, 1, 0);
>    make_decl_rtl (ftn_main);
> -  init_function_start (ftn_main);
> +  allocate_struct_function (ftn_main, false);
>    pushlevel ();
>
>    gfc_init_block (&body);
> @@ -5537,7 +5537,7 @@ gfc_generate_constructors (void)
>
>    make_decl_rtl (fndecl);
>
> -  init_function_start (fndecl);
> +  allocate_struct_function (fndecl, false);
>
>    pushlevel ();
>
>
>         Jakub
Jakub Jelinek Aug. 23, 2012, 9:38 a.m. UTC | #2
On Thu, Aug 23, 2012 at 11:24:26AM +0200, Richard Guenther wrote:
> > Perhaps it would make sense to commit the fortran/ change separately to fix
> > the leak in there, and resolve the rest in a follow-up when it is clear what
> > to do in the C++ FE.
> 
> Yes.  Does the copy_node change pass testing?  If so then installing it
> would be nice as well.

The whole patch passed testing, but in the C++ FE (the cp/decl.c) it is
probably just a partial fix or even wrong fix (e.g., even when we don't
allocate a new cfun there, we still overwrite cfun->language with newly
GCed memory, etc.

So I'm going to commit now the tree.c and trans-decl.c hunks and
will leave the rest for later.

> > 2012-08-23  Jakub Jelinek  <jakub@redhat.com>
> >
> >         * function.c (allocate_struct_function): Assert that
> >         DECL_STRUCT_FUNCTION (fndecl) is NULL.
> >         * tree.c (copy_node_stat): Clear DECL_STRUCT_FUNCTION.
> > fortran/
> >         * trans-decl.c (trans_function_start, generate_coarray_init,
> >         create_main_function, gfc_generate_constructors): Call
> >         allocate_struct_function instead of init_function_start.
> > cp/
> >         * decl.c (start_preparsed_function): Don't call
> >         allocate_struct_function if already allocated.

	Jakub
diff mbox

Patch

--- gcc/cp/decl.c.jj	2012-08-22 00:12:55.000000000 +0200
+++ gcc/cp/decl.c	2012-08-22 22:35:24.040391935 +0200
@@ -12882,7 +12882,10 @@  start_preparsed_function (tree decl1, tr
      CFUN set up, and our per-function variables initialized.
      FIXME factor out the non-RTL stuff.  */
   bl = current_binding_level;
-  allocate_struct_function (decl1, processing_template_decl);
+  if (DECL_STRUCT_FUNCTION (decl1) == NULL)
+    allocate_struct_function (decl1, processing_template_decl);
+  else
+    set_cfun (DECL_STRUCT_FUNCTION (decl1));
 
   /* Initialize the language data structures.  Whenever we start
      a new function, we destroy temporaries in the usual way.  */
--- gcc/function.c.jj	2012-08-22 22:16:07.199445894 +0200
+++ gcc/function.c	2012-08-22 22:37:15.449893843 +0200
@@ -4479,6 +4479,7 @@  allocate_struct_function (tree fndecl, b
 
   if (fndecl != NULL_TREE)
     {
+      gcc_checking_assert (DECL_STRUCT_FUNCTION (fndecl) == NULL);
       DECL_STRUCT_FUNCTION (fndecl) = cfun;
       cfun->decl = fndecl;
       current_function_funcdef_no = get_next_funcdef_no ();
--- gcc/tree.c.jj	2012-08-22 22:16:07.175445954 +0200
+++ gcc/tree.c	2012-08-22 22:37:15.451893834 +0200
@@ -983,6 +983,8 @@  copy_node_stat (tree node MEM_STAT_DECL)
 	  SET_DECL_INIT_PRIORITY (t, DECL_INIT_PRIORITY (node));
 	  DECL_HAS_INIT_PRIORITY_P (t) = 1;
 	}
+      if (TREE_CODE (node) == FUNCTION_DECL)
+	DECL_STRUCT_FUNCTION (t) = NULL;
     }
   else if (TREE_CODE_CLASS (code) == tcc_type)
     {
--- gcc/fortran/trans-decl.c.jj	2012-08-22 14:55:21.358138432 +0200
+++ gcc/fortran/trans-decl.c	2012-08-22 22:37:15.476893717 +0200
@@ -2265,7 +2265,7 @@  trans_function_start (gfc_symbol * sym)
   /* Create RTL for function definition.  */
   make_decl_rtl (fndecl);
 
-  init_function_start (fndecl);
+  allocate_struct_function (fndecl, false);
 
   /* function.c requires a push at the start of the function.  */
   pushlevel ();
@@ -4408,7 +4408,7 @@  generate_coarray_init (gfc_namespace * n
 
   rest_of_decl_compilation (fndecl, 0, 0);
   make_decl_rtl (fndecl);
-  init_function_start (fndecl);
+  allocate_struct_function (fndecl, false);
 
   pushlevel ();
   gfc_init_block (&caf_init_block);
@@ -4982,7 +4982,7 @@  create_main_function (tree fndecl)
 
   rest_of_decl_compilation (ftn_main, 1, 0);
   make_decl_rtl (ftn_main);
-  init_function_start (ftn_main);
+  allocate_struct_function (ftn_main, false);
   pushlevel ();
 
   gfc_init_block (&body);
@@ -5537,7 +5537,7 @@  gfc_generate_constructors (void)
 
   make_decl_rtl (fndecl);
 
-  init_function_start (fndecl);
+  allocate_struct_function (fndecl, false);
 
   pushlevel ();