This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[3.4 PATCH] Fix inline nested functions (PR middle-end/15345, c/16450)
- From: Jakub Jelinek <jakub at redhat dot com>
- To: Richard Henderson <rth at redhat dot com>, mark at codesourcery dot com
- Cc: jh at suse dot cz, gcc-patches at gcc dot gnu dot org
- Date: Tue, 20 Jul 2004 11:22:11 -0400
- Subject: [3.4 PATCH] Fix inline nested functions (PR middle-end/15345, c/16450)
- Reply-to: Jakub Jelinek <jakub at redhat dot com>
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