diff mbox series

c++: Ensure some more that immediate functions aren't gimplified [PR103912]

Message ID 20220106084808.GW2646553@tucnak
State New
Headers show
Series c++: Ensure some more that immediate functions aren't gimplified [PR103912] | expand

Commit Message

Jakub Jelinek Jan. 6, 2022, 8:48 a.m. UTC
Hi!

Immediate functions should never be emitted into assembly, the FE doesn't
genericize them and does various things to ensure they aren't gimplified.
But the following testcase ICEs anyway due to that, because the consteval
function returns a lambda, and operator() of the lambda has
decl_function_context of the consteval function.  cgraphunit.c then
does:
              /* Preserve a functions function context node.  It will
                 later be needed to output debug info.  */
              if (tree fn = decl_function_context (decl))
                {
                  cgraph_node *origin_node = cgraph_node::get_create (fn);
                  enqueue_node (origin_node);
                }
which enqueues the immediate function and then tries to gimplify it,
which results in ICE because it hasn't been genericized.

When I try similar testcase with constexpr instead of consteval and
static constinit auto instead of auto in main, what happens is that
the functions are gimplified, later ipa.c discovers they aren't reachable
and sets body_removed to true for them (and clears other flags) and we end
up with a debug info which has the foo and bar functions without
DW_AT_low_pc and other code specific attributes, just stuff from its BLOCK
structure and in there the lambda with DW_AT_low_pc etc.

The following patch attempts to emulate that behavior early, so that cgraph
doesn't try to gimplify those and pretends they were already gimplified
and found unused and optimized away.

Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

2022-01-06  Jakub Jelinek  <jakub@redhat.com>

	PR c++/103912
	* semantics.c (expand_or_defer_fn): For immediate functions, set
	node->body_removed to true and clear analyzed, definition and
	force_output.
	* decl2.c (c_parse_final_cleanups): Ignore immediate functions for
	expand_or_defer_fn.

	* g++.dg/cpp2a/consteval26.C: New test.


	Jakub

Comments

Jason Merrill Jan. 10, 2022, 7 p.m. UTC | #1
On 1/6/22 03:48, Jakub Jelinek wrote:
> Hi!
> 
> Immediate functions should never be emitted into assembly, the FE doesn't
> genericize them and does various things to ensure they aren't gimplified.
> But the following testcase ICEs anyway due to that, because the consteval
> function returns a lambda, and operator() of the lambda has
> decl_function_context of the consteval function.  cgraphunit.c then
> does:
>                /* Preserve a functions function context node.  It will
>                   later be needed to output debug info.  */
>                if (tree fn = decl_function_context (decl))
>                  {
>                    cgraph_node *origin_node = cgraph_node::get_create (fn);
>                    enqueue_node (origin_node);
>                  }
> which enqueues the immediate function and then tries to gimplify it,
> which results in ICE because it hasn't been genericized.
> 
> When I try similar testcase with constexpr instead of consteval and
> static constinit auto instead of auto in main, what happens is that
> the functions are gimplified, later ipa.c discovers they aren't reachable
> and sets body_removed to true for them (and clears other flags) and we end
> up with a debug info which has the foo and bar functions without
> DW_AT_low_pc and other code specific attributes, just stuff from its BLOCK
> structure and in there the lambda with DW_AT_low_pc etc.
> 
> The following patch attempts to emulate that behavior early, so that cgraph
> doesn't try to gimplify those and pretends they were already gimplified
> and found unused and optimized away.
> 
> Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?
> 
> 2022-01-06  Jakub Jelinek  <jakub@redhat.com>
> 
> 	PR c++/103912
> 	* semantics.c (expand_or_defer_fn): For immediate functions, set
> 	node->body_removed to true and clear analyzed, definition and
> 	force_output.
> 	* decl2.c (c_parse_final_cleanups): Ignore immediate functions for
> 	expand_or_defer_fn.
> 
> 	* g++.dg/cpp2a/consteval26.C: New test.
> 
> --- gcc/cp/semantics.c.jj	2022-01-03 10:40:48.000000000 +0100
> +++ gcc/cp/semantics.c	2022-01-05 12:52:11.484379138 +0100
> @@ -4785,6 +4785,18 @@ expand_or_defer_fn (tree fn)
>         emit_associated_thunks (fn);
>   
>         function_depth--;
> +
> +      if (DECL_IMMEDIATE_FUNCTION_P (fn))
> +	{
> +	  cgraph_node *node = cgraph_node::get (fn);
> +	  if (node)

This can be

if (cgraph_node *node = cgraph_node::get (fn))

OK either way.

> +	    {
> +	      node->body_removed = true;
> +	      node->analyzed = false;
> +	      node->definition = false;
> +	      node->force_output = false;
> +	    }
> +	}
>       }
>   }
>   
> --- gcc/cp/decl2.c.jj	2022-01-03 10:40:48.083068010 +0100
> +++ gcc/cp/decl2.c	2022-01-05 12:53:34.930204119 +0100
> @@ -5272,6 +5272,7 @@ c_parse_final_cleanups (void)
>   	  if (!DECL_EXTERNAL (decl)
>   	      && decl_needed_p (decl)
>   	      && !TREE_ASM_WRITTEN (decl)
> +	      && !DECL_IMMEDIATE_FUNCTION_P (decl)
>   	      && !node->definition)
>   	    {
>   	      /* We will output the function; no longer consider it in this
> --- gcc/testsuite/g++.dg/cpp2a/consteval26.C.jj	2022-01-05 12:42:07.918878074 +0100
> +++ gcc/testsuite/g++.dg/cpp2a/consteval26.C	2022-01-05 12:40:18.853416637 +0100
> @@ -0,0 +1,39 @@
> +// PR c++/103912
> +// { dg-do run { target c++20 } }
> +// { dg-additional-options "-O2 -g -fkeep-inline-functions" }
> +
> +extern "C" void abort ();
> +
> +struct A { A () {} };
> +
> +consteval auto
> +foo ()
> +{
> +  if (1)
> +    ;
> +  return [] (A x) { return 1; };
> +}
> +
> +consteval auto
> +bar (int a)
> +{
> +  int b = a + 4;
> +  if (1)
> +    ;
> +  return [=] (A x) { return a + b; };
> +}
> +
> +int
> +main ()
> +{
> +  A x;
> +  auto h = foo ();
> +  if (h (x) != 1)
> +    abort ();
> +  auto i = bar (5);
> +  if (i (x) != 14)
> +    abort ();
> +  auto j = bar (42);
> +  if (j (x) != 88)
> +    abort ();
> +}
> 
> 	Jakub
>
diff mbox series

Patch

--- gcc/cp/semantics.c.jj	2022-01-03 10:40:48.000000000 +0100
+++ gcc/cp/semantics.c	2022-01-05 12:52:11.484379138 +0100
@@ -4785,6 +4785,18 @@  expand_or_defer_fn (tree fn)
       emit_associated_thunks (fn);
 
       function_depth--;
+
+      if (DECL_IMMEDIATE_FUNCTION_P (fn))
+	{
+	  cgraph_node *node = cgraph_node::get (fn);
+	  if (node)
+	    {
+	      node->body_removed = true;
+	      node->analyzed = false;
+	      node->definition = false;
+	      node->force_output = false;
+	    }
+	}
     }
 }
 
--- gcc/cp/decl2.c.jj	2022-01-03 10:40:48.083068010 +0100
+++ gcc/cp/decl2.c	2022-01-05 12:53:34.930204119 +0100
@@ -5272,6 +5272,7 @@  c_parse_final_cleanups (void)
 	  if (!DECL_EXTERNAL (decl)
 	      && decl_needed_p (decl)
 	      && !TREE_ASM_WRITTEN (decl)
+	      && !DECL_IMMEDIATE_FUNCTION_P (decl)
 	      && !node->definition)
 	    {
 	      /* We will output the function; no longer consider it in this
--- gcc/testsuite/g++.dg/cpp2a/consteval26.C.jj	2022-01-05 12:42:07.918878074 +0100
+++ gcc/testsuite/g++.dg/cpp2a/consteval26.C	2022-01-05 12:40:18.853416637 +0100
@@ -0,0 +1,39 @@ 
+// PR c++/103912
+// { dg-do run { target c++20 } }
+// { dg-additional-options "-O2 -g -fkeep-inline-functions" }
+
+extern "C" void abort ();
+
+struct A { A () {} };
+
+consteval auto
+foo ()
+{
+  if (1)
+    ;
+  return [] (A x) { return 1; };
+}
+
+consteval auto
+bar (int a)
+{
+  int b = a + 4;
+  if (1)
+    ;
+  return [=] (A x) { return a + b; };
+}
+
+int
+main ()
+{
+  A x;
+  auto h = foo ();
+  if (h (x) != 1)
+    abort ();
+  auto i = bar (5);
+  if (i (x) != 14)
+    abort ();
+  auto j = bar (42);
+  if (j (x) != 88)
+    abort ();
+}