This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[3.4 PATCH] Fix inline nested functions (PR middle-end/15345, c/16450)


Hi!

Unlike GCC 3.3.x or HEAD, GCC 3.4.x emits nested inline function
bodies even when they are unused, or successfully inlined in all places.
This is a big problem e.g. for GLIBC dynamic linker, as it increases
its size a lot.

I have bootstrapped/regtested the following patch on
{i386,x86_64,ia64,ppc,ppc64,s390,s390x}-*-linux*.
Ok for gcc-3_4-branch?

2004-07-20  Jakub Jelinek  <jakub@redhat.com>
	    Jan Hubicka  <jh@suse.cz>

	PR middle-end/15345
	PR c/16450
	* toplev.c (rest_of_handle_inlining): Set DECL_DEFER_OUTPUT on C
	nested functions as well.
	* tree-optimize.c (tree_rest_of_compilation): Don't clear decl rtls
	for deferred nested inlines.

	* gcc.dg/torture/nested-fn-1.c: New test.

--- gcc/toplev.c.jj	2004-02-20 19:37:45.000000000 +0100
+++ gcc/toplev.c	2004-07-19 18:48:59.000000000 +0200
@@ -2714,9 +2714,11 @@ rest_of_handle_inlining (tree decl)
 	     for unreferenced symbols.  See g77.f-torture/execute/980520-1.f.
 	     But removing this line from the check breaks all languages that
 	     use the call graph to output symbols.  This hard-coded check is
-	     the least invasive work-around.  */
+	     the least invasive work-around.  Nested functions need to be
+	     deferred too.  */
 	  && (flag_inline_functions
-	      || strcmp (lang_hooks.name, "GNU F77") == 0)
+	      || strcmp (lang_hooks.name, "GNU F77") == 0
+	      || (cgraph_n_nodes > 0 && cgraph_node (decl)->origin))
 	  && ((! TREE_PUBLIC (decl) && ! TREE_ADDRESSABLE (decl)
 	       && ! TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (decl))
 	       && ! flag_keep_inline_functions)
--- gcc/tree-optimize.c.jj	2004-02-20 19:37:45.000000000 +0100
+++ gcc/tree-optimize.c	2004-07-20 11:40:23.967729483 +0200
@@ -195,29 +195,33 @@ tree_rest_of_compilation (tree fndecl, b
 	}
     }
 
-  /* Since we don't need the RTL for this function anymore, stop pointing to
-     it.  That's especially important for LABEL_DECLs, since you can reach all
-     the instructions in the function from the CODE_LABEL stored in the
-     DECL_RTL for the LABEL_DECL.  Walk the BLOCK-tree, clearing DECL_RTL for
-     LABEL_DECLs and non-static local variables.  Note that we must check the
-     context of the variables, otherwise processing a nested function can kill
-     the rtl of a variable from an outer function.  */
-  walk_tree_without_duplicates (&DECL_SAVED_TREE (fndecl),
-				clear_decl_rtl,
-				fndecl);
-  if (!cgraph_function_possibly_inlined_p (fndecl))
+  if (! DECL_DEFER_OUTPUT (fndecl) || !cgraph_node (fndecl)->origin)
     {
-      DECL_SAVED_TREE (fndecl) = NULL;
-      if (DECL_SAVED_INSNS (fndecl) == 0
-	  && !cgraph_node (fndecl)->origin)
+      /* Since we don't need the RTL for this function anymore, stop pointing
+	 to it.  That's especially important for LABEL_DECLs, since you can
+	 reach all the instructions in the function from the CODE_LABEL stored
+	 in the DECL_RTL for the LABEL_DECL.  Walk the BLOCK-tree, clearing
+	 DECL_RTL for LABEL_DECLs and non-static local variables.  Note that
+	 we must check the context of the variables, otherwise processing a
+	 nested function can kill the rtl of a variable from an outer
+	 function.  */
+      walk_tree_without_duplicates (&DECL_SAVED_TREE (fndecl),
+				    clear_decl_rtl,
+				    fndecl);
+      if (!cgraph_function_possibly_inlined_p (fndecl))
 	{
-	  /* Stop pointing to the local nodes about to be freed.
-	     But DECL_INITIAL must remain nonzero so we know this
-	     was an actual function definition.
-	     For a nested function, this is done in c_pop_function_context.
-	     If rest_of_compilation set this to 0, leave it 0.  */
-	  if (DECL_INITIAL (fndecl) != 0)
-	    DECL_INITIAL (fndecl) = error_mark_node;
+	  DECL_SAVED_TREE (fndecl) = NULL;
+	  if (DECL_SAVED_INSNS (fndecl) == 0
+	      && !cgraph_node (fndecl)->origin)
+	    {
+	      /* Stop pointing to the local nodes about to be freed.
+		 But DECL_INITIAL must remain nonzero so we know this
+		 was an actual function definition.
+		 For a nested function, this is done in c_pop_function_context.
+		 If rest_of_compilation set this to 0, leave it 0.  */
+	      if (DECL_INITIAL (fndecl) != 0)
+		DECL_INITIAL (fndecl) = error_mark_node;
+	    }
 	}
     }
 
--- gcc/testsuite/gcc.dg/torture/nested-fn-1.c.jj	2004-07-19 18:25:45.000000000 +0200
+++ gcc/testsuite/gcc.dg/torture/nested-fn-1.c	2004-07-20 11:48:41.052482823 +0200
@@ -0,0 +1,73 @@
+/* PR middle-end/15345, c/16450 */
+/* Test whether unused nested functions aren't emitted into the assembly.  */
+/* { dg-do compile } */
+/* { dg-options "-g0" } */
+
+int
+fn1 (int x)
+{
+  int i = x;
+  inline __attribute__((always_inline)) int
+  should_not_appear1 (void)
+  {
+    return i;
+  }
+  return should_not_appear1 ();
+}
+
+int
+fn2 (int x)
+{
+  int i = x;
+  inline __attribute__((always_inline)) int
+  should_not_appear2 (void)
+  {
+    return i;
+  }
+  return x;
+}
+
+extern void check (void *p);
+
+int
+fn3 (int x)
+{
+  int i = x;
+  inline int
+  should_appear1 (void)
+  {
+    char *p = __builtin_alloca (i);
+    check (p);
+    return i;
+  }
+  return should_appear1 ();
+}
+
+int
+fn4 (int x)
+{
+  int i = x;
+  inline int
+  should_not_appear3 (void)
+  {
+    char *p = __builtin_alloca (i);
+    check (p);
+    return i;
+  }
+  return 0 ? should_not_appear3 () : 1;
+}
+
+int
+fn5 (int x)
+{
+  int i = x;
+  inline int
+  should_appear2 (void)
+  {
+    return i;
+  }
+  check (should_appear2);
+  return i;
+}
+
+/* { dg-final { scan-assembler-not "should_not_appear" } } */


	Jakub


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]