Patchwork Fix handling of const/pure noreturn functions (PR tree-optimization/44485)

login
register
mail settings
Submitter Jakub Jelinek
Date Aug. 26, 2010, 3 p.m.
Message ID <20100826150050.GC702@tyan-ft48-01.lab.bos.redhat.com>
Download mbox | patch
Permalink /patch/62782/
State New
Headers show

Comments

Jakub Jelinek - Aug. 26, 2010, 3 p.m.
Hi!

On this testcase, local_pure_const discovery first determines bar
is TREE_READONLY/DECL_LOOPING_CONST_OR_PURE_P and only later on
finds out it is also noreturn (TREE_THIS_VOLATILE).
flags_from_decl_or_type in that case would not return ECF_CONST, which means
a call which didn't have vops suddenly needs them and the calling pass of
fixup_noreturn_call doesn't expect .MEM needs to be renamed.
Fixed by treating noreturn const or pure as noreturn const/pure looping
functions.  I wanted to make sure DECL_LOOPING_CONST_OR_PURE_P is
going to be set for noreturn const or pure functions declared with
attribute, but that would be quite difficult (would need to handle that
in all the pure/const/noreturn attribute handling, and in C and C++ decl
merging, etc.).  There are only very few places which check
DECL_LOOPING_CONST_OR_PURE_P directly, it is easier just to use
flags_from_decl_or_type there as most of the other places already do.

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

2010-08-26  Jakub Jelinek  <jakub@redhat.com>

	PR tree-optimization/44485
	* calls.c (flags_from_decl_or_type): For const or pure
	noreturn functions return ECF_LOOPING_CONST_OR_PURE|ECF_NORETURN
	together with ECF_CONST resp. ECF_PURE.
	* builtins.c (expand_builtin): Use flags_from_decl_or_type
	instead of querying flags directly.
	* tree-ssa-loop-niter.c (finite_loop_p): Likewise.
	* tree-ssa-dce.c (find_obviously_necessary_stmts): Likewise.

	* gcc.dg/pr44485.c: New test.


	Jakub
Richard Guenther - Aug. 26, 2010, 3:11 p.m.
On Thu, Aug 26, 2010 at 5:00 PM, Jakub Jelinek <jakub@redhat.com> wrote:
> Hi!
>
> On this testcase, local_pure_const discovery first determines bar
> is TREE_READONLY/DECL_LOOPING_CONST_OR_PURE_P and only later on
> finds out it is also noreturn (TREE_THIS_VOLATILE).
> flags_from_decl_or_type in that case would not return ECF_CONST, which means
> a call which didn't have vops suddenly needs them and the calling pass of
> fixup_noreturn_call doesn't expect .MEM needs to be renamed.
> Fixed by treating noreturn const or pure as noreturn const/pure looping
> functions.  I wanted to make sure DECL_LOOPING_CONST_OR_PURE_P is
> going to be set for noreturn const or pure functions declared with
> attribute, but that would be quite difficult (would need to handle that
> in all the pure/const/noreturn attribute handling, and in C and C++ decl
> merging, etc.).  There are only very few places which check
> DECL_LOOPING_CONST_OR_PURE_P directly, it is easier just to use
> flags_from_decl_or_type there as most of the other places already do.
>
> Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

Ok.

Thanks,
Richard.

> 2010-08-26  Jakub Jelinek  <jakub@redhat.com>
>
>        PR tree-optimization/44485
>        * calls.c (flags_from_decl_or_type): For const or pure
>        noreturn functions return ECF_LOOPING_CONST_OR_PURE|ECF_NORETURN
>        together with ECF_CONST resp. ECF_PURE.
>        * builtins.c (expand_builtin): Use flags_from_decl_or_type
>        instead of querying flags directly.
>        * tree-ssa-loop-niter.c (finite_loop_p): Likewise.
>        * tree-ssa-dce.c (find_obviously_necessary_stmts): Likewise.
>
>        * gcc.dg/pr44485.c: New test.
>
> --- gcc/calls.c.jj      2010-08-16 19:24:16.000000000 +0200
> +++ gcc/calls.c 2010-08-26 12:21:17.000000000 +0200
> @@ -601,7 +601,7 @@ flags_from_decl_or_type (const_tree exp)
>        flags |= ECF_RETURNS_TWICE;
>
>       /* Process the pure and const attributes.  */
> -      if (TREE_READONLY (exp) && ! TREE_THIS_VOLATILE (exp))
> +      if (TREE_READONLY (exp))
>        flags |= ECF_CONST;
>       if (DECL_PURE_P (exp))
>        flags |= ECF_PURE;
> @@ -616,11 +616,15 @@ flags_from_decl_or_type (const_tree exp)
>
>       flags = special_function_p (exp, flags);
>     }
> -  else if (TYPE_P (exp) && TYPE_READONLY (exp) && ! TREE_THIS_VOLATILE (exp))
> +  else if (TYPE_P (exp) && TYPE_READONLY (exp))
>     flags |= ECF_CONST;
>
>   if (TREE_THIS_VOLATILE (exp))
> -    flags |= ECF_NORETURN;
> +    {
> +      flags |= ECF_NORETURN;
> +      if (flags & (ECF_CONST|ECF_PURE))
> +       flags |= ECF_LOOPING_CONST_OR_PURE;
> +    }
>
>   return flags;
>  }
> --- gcc/builtins.c.jj   2010-08-20 16:51:09.000000000 +0200
> +++ gcc/builtins.c      2010-08-26 13:59:07.000000000 +0200
> @@ -5748,6 +5748,7 @@ expand_builtin (tree exp, rtx target, rt
>   tree fndecl = get_callee_fndecl (exp);
>   enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
>   enum machine_mode target_mode = TYPE_MODE (TREE_TYPE (exp));
> +  int flags;
>
>   if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD)
>     return targetm.expand_builtin (exp, target, subtarget, mode, ignore);
> @@ -5770,8 +5771,8 @@ expand_builtin (tree exp, rtx target, rt
>      none of its arguments are volatile, we can avoid expanding the
>      built-in call and just evaluate the arguments for side-effects.  */
>   if (target == const0_rtx
> -      && (DECL_PURE_P (fndecl) || TREE_READONLY (fndecl))
> -      && !DECL_LOOPING_CONST_OR_PURE_P (fndecl))
> +      && ((flags = flags_from_decl_or_type (fndecl)) & (ECF_CONST | ECF_PURE))
> +      && !(flags & ECF_LOOPING_CONST_OR_PURE))
>     {
>       bool volatilep = false;
>       tree arg;
> --- gcc/tree-ssa-loop-niter.c.jj        2010-08-20 16:05:40.000000000 +0200
> +++ gcc/tree-ssa-loop-niter.c   2010-08-26 13:59:04.000000000 +0200
> @@ -1970,12 +1970,12 @@ finite_loop_p (struct loop *loop)
>   edge ex;
>   struct tree_niter_desc desc;
>   bool finite = false;
> +  int flags;
>
>   if (flag_unsafe_loop_optimizations)
>     return true;
> -  if ((TREE_READONLY (current_function_decl)
> -       || DECL_PURE_P (current_function_decl))
> -      && !DECL_LOOPING_CONST_OR_PURE_P (current_function_decl))
> +  flags = flags_from_decl_or_type (current_function_decl);
> +  if ((flags & (ECF_CONST|ECF_PURE)) && !(flags & ECF_LOOPING_CONST_OR_PURE))
>     {
>       if (dump_file && (dump_flags & TDF_DETAILS))
>        fprintf (dump_file, "Found loop %i to be finite: it is within pure or const function.\n",
> --- gcc/tree-ssa-dce.c.jj       2010-07-05 12:37:02.000000000 +0200
> +++ gcc/tree-ssa-dce.c  2010-08-26 13:59:01.000000000 +0200
> @@ -433,6 +433,7 @@ find_obviously_necessary_stmts (struct e
>   gimple_stmt_iterator gsi;
>   edge e;
>   gimple phi, stmt;
> +  int flags;
>
>   FOR_EACH_BB (bb)
>     {
> @@ -454,9 +455,8 @@ find_obviously_necessary_stmts (struct e
>
>   /* Pure and const functions are finite and thus have no infinite loops in
>      them.  */
> -  if ((TREE_READONLY (current_function_decl)
> -       || DECL_PURE_P (current_function_decl))
> -      && !DECL_LOOPING_CONST_OR_PURE_P (current_function_decl))
> +  flags = flags_from_decl_or_type (current_function_decl);
> +  if ((flags & (ECF_CONST|ECF_PURE)) && !(flags & ECF_LOOPING_CONST_OR_PURE))
>     return;
>
>   /* Prevent the empty possibly infinite loops from being removed.  */
> --- gcc/testsuite/gcc.dg/pr44485.c.jj   2010-08-26 13:59:52.000000000 +0200
> +++ gcc/testsuite/gcc.dg/pr44485.c      2010-08-26 13:57:32.000000000 +0200
> @@ -0,0 +1,31 @@
> +/* PR tree-optimization/44485 */
> +/* { dg-do compile } */
> +/* { dg-options "-O1 -funsafe-math-optimizations" } */
> +
> +unsigned short b;
> +int bar (unsigned);
> +
> +void
> +baz (void)
> +{
> +  if (bar (0))
> +    for (b = 0; b < 30; b++)
> +      ;
> +}
> +
> +int
> +bar (unsigned z)
> +{
> +  unsigned short c;
> +  for (; ; z += 1)
> +l1:
> +    if (z)
> +      goto l2;
> +l2:
> +  for (z = 0; z < 9; z++)
> +    if (z)
> +      goto l1;
> +  for (c = 0; c; c = (__UINTPTR_TYPE__) baz)
> +    ;
> +  return 0;
> +}
>
>        Jakub
>

Patch

--- gcc/calls.c.jj	2010-08-16 19:24:16.000000000 +0200
+++ gcc/calls.c	2010-08-26 12:21:17.000000000 +0200
@@ -601,7 +601,7 @@  flags_from_decl_or_type (const_tree exp)
 	flags |= ECF_RETURNS_TWICE;
 
       /* Process the pure and const attributes.  */
-      if (TREE_READONLY (exp) && ! TREE_THIS_VOLATILE (exp))
+      if (TREE_READONLY (exp))
 	flags |= ECF_CONST;
       if (DECL_PURE_P (exp))
 	flags |= ECF_PURE;
@@ -616,11 +616,15 @@  flags_from_decl_or_type (const_tree exp)
 
       flags = special_function_p (exp, flags);
     }
-  else if (TYPE_P (exp) && TYPE_READONLY (exp) && ! TREE_THIS_VOLATILE (exp))
+  else if (TYPE_P (exp) && TYPE_READONLY (exp))
     flags |= ECF_CONST;
 
   if (TREE_THIS_VOLATILE (exp))
-    flags |= ECF_NORETURN;
+    {
+      flags |= ECF_NORETURN;
+      if (flags & (ECF_CONST|ECF_PURE))
+	flags |= ECF_LOOPING_CONST_OR_PURE;
+    }
 
   return flags;
 }
--- gcc/builtins.c.jj	2010-08-20 16:51:09.000000000 +0200
+++ gcc/builtins.c	2010-08-26 13:59:07.000000000 +0200
@@ -5748,6 +5748,7 @@  expand_builtin (tree exp, rtx target, rt
   tree fndecl = get_callee_fndecl (exp);
   enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
   enum machine_mode target_mode = TYPE_MODE (TREE_TYPE (exp));
+  int flags;
 
   if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD)
     return targetm.expand_builtin (exp, target, subtarget, mode, ignore);
@@ -5770,8 +5771,8 @@  expand_builtin (tree exp, rtx target, rt
      none of its arguments are volatile, we can avoid expanding the
      built-in call and just evaluate the arguments for side-effects.  */
   if (target == const0_rtx
-      && (DECL_PURE_P (fndecl) || TREE_READONLY (fndecl))
-      && !DECL_LOOPING_CONST_OR_PURE_P (fndecl))
+      && ((flags = flags_from_decl_or_type (fndecl)) & (ECF_CONST | ECF_PURE))
+      && !(flags & ECF_LOOPING_CONST_OR_PURE))
     {
       bool volatilep = false;
       tree arg;
--- gcc/tree-ssa-loop-niter.c.jj	2010-08-20 16:05:40.000000000 +0200
+++ gcc/tree-ssa-loop-niter.c	2010-08-26 13:59:04.000000000 +0200
@@ -1970,12 +1970,12 @@  finite_loop_p (struct loop *loop)
   edge ex;
   struct tree_niter_desc desc;
   bool finite = false;
+  int flags;
 
   if (flag_unsafe_loop_optimizations)
     return true;
-  if ((TREE_READONLY (current_function_decl)
-       || DECL_PURE_P (current_function_decl))
-      && !DECL_LOOPING_CONST_OR_PURE_P (current_function_decl))
+  flags = flags_from_decl_or_type (current_function_decl);
+  if ((flags & (ECF_CONST|ECF_PURE)) && !(flags & ECF_LOOPING_CONST_OR_PURE))
     {
       if (dump_file && (dump_flags & TDF_DETAILS))
 	fprintf (dump_file, "Found loop %i to be finite: it is within pure or const function.\n",
--- gcc/tree-ssa-dce.c.jj	2010-07-05 12:37:02.000000000 +0200
+++ gcc/tree-ssa-dce.c	2010-08-26 13:59:01.000000000 +0200
@@ -433,6 +433,7 @@  find_obviously_necessary_stmts (struct e
   gimple_stmt_iterator gsi;
   edge e;
   gimple phi, stmt;
+  int flags;
 
   FOR_EACH_BB (bb)
     {
@@ -454,9 +455,8 @@  find_obviously_necessary_stmts (struct e
 
   /* Pure and const functions are finite and thus have no infinite loops in
      them.  */
-  if ((TREE_READONLY (current_function_decl)
-       || DECL_PURE_P (current_function_decl))
-      && !DECL_LOOPING_CONST_OR_PURE_P (current_function_decl))
+  flags = flags_from_decl_or_type (current_function_decl);
+  if ((flags & (ECF_CONST|ECF_PURE)) && !(flags & ECF_LOOPING_CONST_OR_PURE))
     return;
 
   /* Prevent the empty possibly infinite loops from being removed.  */
--- gcc/testsuite/gcc.dg/pr44485.c.jj	2010-08-26 13:59:52.000000000 +0200
+++ gcc/testsuite/gcc.dg/pr44485.c	2010-08-26 13:57:32.000000000 +0200
@@ -0,0 +1,31 @@ 
+/* PR tree-optimization/44485 */
+/* { dg-do compile } */
+/* { dg-options "-O1 -funsafe-math-optimizations" } */
+
+unsigned short b;
+int bar (unsigned);
+
+void
+baz (void)
+{
+  if (bar (0))
+    for (b = 0; b < 30; b++)
+      ;
+}
+
+int
+bar (unsigned z)
+{
+  unsigned short c;
+  for (; ; z += 1)
+l1:
+    if (z)
+      goto l2;
+l2:
+  for (z = 0; z < 9; z++)
+    if (z)
+      goto l1;
+  for (c = 0; c; c = (__UINTPTR_TYPE__) baz)
+    ;
+  return 0;
+}