The call check in optimize_stack_restore is not the same as
what is described in the comment before the function. It has never
been the same even. It has always allowed all nromal builtins but not
target builtins while the comment says no calls.

This rewrites it to allow the following.
* a restore right before a noreturn is fine to be removed as the noreturn
  function will do the restore (or exit the program).
* Internal functions are ok because they will turn into an instruction or 2
* Still specifically reject alloca and __scrub_leave
* simple or inexpensive builtins (including target builtins)

This will both reject some more locations but also accepts more locations due
to the internal and target and noreturn function calls checks.

Changes since v1:
* v2: add comment on why calls in general should not be ignored.

Bootstrapped and tested on x86_64-linux-gnu.

        PR tree-optimization/122033
gcc/ChangeLog:

        * tree-ssa-ccp.cc (optimize_stack_restore): Rewrite the call check.
        Update comment in the front to new rules on calls.
        * tree.h (fndecl_builtin_alloc_p): New function.

gcc/testsuite/ChangeLog:

        * gcc.dg/tree-ssa/pr122033-1.c: New test.
        * gcc.dg/tree-ssa/pr122033-2.c: New test.

Signed-off-by: Andrew Pinski <[email protected]>
---
 gcc/gimple-fold.cc                         |   6 +
 gcc/testsuite/gcc.dg/tree-ssa/pr122033-1.c |  18 +++
 gcc/testsuite/gcc.dg/tree-ssa/pr122033-2.c |  23 ++++
 gcc/tree-ssa-ccp.cc                        | 123 +++++----------------
 gcc/tree-ssa-forwprop.cc                   | 111 +++++++++++++++++++
 gcc/tree.h                                 |   9 ++
 6 files changed, 194 insertions(+), 96 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr122033-1.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr122033-2.c

diff --git a/gcc/gimple-fold.cc b/gcc/gimple-fold.cc
index 37ca0853ec8..a12fb5e8eab 100644
--- a/gcc/gimple-fold.cc
+++ b/gcc/gimple-fold.cc
@@ -5886,6 +5886,12 @@ gimple_fold_call (gimple_stmt_iterator *gsi, bool 
inplace)
       tree overflow = NULL_TREE;
       switch (gimple_call_internal_fn (stmt))
        {
+       case IFN_ASSUME:
+         /* Remove .ASSUME calls during the last fold since it is no
+            longer needed.  */
+         if (cfun->curr_properties & PROP_last_full_fold)
+           replace_call_with_value (gsi, NULL_TREE);
+         break;
        case IFN_BUILTIN_EXPECT:
          result = fold_builtin_expect (gimple_location (stmt),
                                        gimple_call_arg (stmt, 0),
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr122033-1.c 
b/gcc/testsuite/gcc.dg/tree-ssa/pr122033-1.c
new file mode 100644
index 00000000000..4ef8c6c3150
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/pr122033-1.c
@@ -0,0 +1,18 @@
+/* PR middle-end/122033 */
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+void bar1 (char *, int);
+void bar3(void) __attribute__((noreturn));
+void foo1 (int size)
+{
+  {
+    char temp[size];
+    temp[size-1] = '\0';
+    bar1 (temp, size);
+  }
+  bar3 ();
+}
+
+/* { dg-final { scan-tree-dump-not "__builtin_stack_save" "optimized"} } */
+/* { dg-final { scan-tree-dump-not "__builtin_stack_restore" "optimized"} } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr122033-2.c 
b/gcc/testsuite/gcc.dg/tree-ssa/pr122033-2.c
new file mode 100644
index 00000000000..f429324f64c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/pr122033-2.c
@@ -0,0 +1,23 @@
+/* PR middle-end/122033 */
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+void g(int*);
+void h();
+double t;
+void f(int a, int b)
+{
+  {
+    int array0[a];
+    {
+      int array1[b];
+      g(array0);
+      g(array1);
+    }
+    t = __builtin_sin(t);
+  }
+  h ();
+}
+
+/* { dg-final { scan-tree-dump-times "__builtin_stack_save" 2 "optimized"} } */
+/* { dg-final { scan-tree-dump-times "__builtin_stack_restore" 2 "optimized"} 
} */
diff --git a/gcc/tree-ssa-ccp.cc b/gcc/tree-ssa-ccp.cc
index c9ffd2af85c..739d3be9129 100644
--- a/gcc/tree-ssa-ccp.cc
+++ b/gcc/tree-ssa-ccp.cc
@@ -3085,93 +3085,6 @@ make_pass_ccp (gcc::context *ctxt)
   return new pass_ccp (ctxt);
 }
 
-
-
-/* Try to optimize out __builtin_stack_restore.  Optimize it out
-   if there is another __builtin_stack_restore in the same basic
-   block and no calls or ASM_EXPRs are in between, or if this block's
-   only outgoing edge is to EXIT_BLOCK and there are no calls or
-   ASM_EXPRs after this __builtin_stack_restore.  */
-
-static tree
-optimize_stack_restore (gimple_stmt_iterator i)
-{
-  tree callee;
-  gimple *stmt;
-
-  basic_block bb = gsi_bb (i);
-  gimple *call = gsi_stmt (i);
-
-  if (gimple_code (call) != GIMPLE_CALL
-      || gimple_call_num_args (call) != 1
-      || TREE_CODE (gimple_call_arg (call, 0)) != SSA_NAME
-      || !POINTER_TYPE_P (TREE_TYPE (gimple_call_arg (call, 0))))
-    return NULL_TREE;
-
-  for (gsi_next (&i); !gsi_end_p (i); gsi_next (&i))
-    {
-      stmt = gsi_stmt (i);
-      if (gimple_code (stmt) == GIMPLE_ASM)
-       return NULL_TREE;
-      if (gimple_code (stmt) != GIMPLE_CALL)
-       continue;
-
-      callee = gimple_call_fndecl (stmt);
-      if (!callee
-         || !fndecl_built_in_p (callee, BUILT_IN_NORMAL)
-         /* All regular builtins are ok, just obviously not alloca.  */
-         || ALLOCA_FUNCTION_CODE_P (DECL_FUNCTION_CODE (callee))
-         /* Do not remove stack updates before strub leave.  */
-         || fndecl_built_in_p (callee, BUILT_IN___STRUB_LEAVE))
-       return NULL_TREE;
-
-      if (fndecl_built_in_p (callee, BUILT_IN_STACK_RESTORE))
-       goto second_stack_restore;
-    }
-
-  if (!gsi_end_p (i))
-    return NULL_TREE;
-
-  /* Allow one successor of the exit block, or zero successors.  */
-  switch (EDGE_COUNT (bb->succs))
-    {
-    case 0:
-      break;
-    case 1:
-      if (single_succ_edge (bb)->dest != EXIT_BLOCK_PTR_FOR_FN (cfun))
-       return NULL_TREE;
-      break;
-    default:
-      return NULL_TREE;
-    }
- second_stack_restore:
-
-  /* If there's exactly one use, then zap the call to __builtin_stack_save.
-     If there are multiple uses, then the last one should remove the call.
-     In any case, whether the call to __builtin_stack_save can be removed
-     or not is irrelevant to removing the call to __builtin_stack_restore.  */
-  if (has_single_use (gimple_call_arg (call, 0)))
-    {
-      gimple *stack_save = SSA_NAME_DEF_STMT (gimple_call_arg (call, 0));
-      if (is_gimple_call (stack_save))
-       {
-         callee = gimple_call_fndecl (stack_save);
-         if (callee && fndecl_built_in_p (callee, BUILT_IN_STACK_SAVE))
-           {
-             gimple_stmt_iterator stack_save_gsi;
-             tree rhs;
-
-             stack_save_gsi = gsi_for_stmt (stack_save);
-             rhs = build_int_cst (TREE_TYPE (gimple_call_arg (call, 0)), 0);
-             replace_call_with_value (&stack_save_gsi, rhs);
-           }
-       }
-    }
-
-  /* No effect, so the statement will be deleted.  */
-  return integer_zero_node;
-}
-
 /* If va_list type is a simple pointer and nothing special is needed,
    optimize __builtin_va_start (&ap, 0) into ap = __builtin_next_arg (0),
    __builtin_va_end (&ap) out as NOP and __builtin_va_copy into a simple
@@ -4278,9 +4191,34 @@ pass_fold_builtins::execute (function *fun)
 
          callee = gimple_call_fndecl (stmt);
          if (!callee
-             && gimple_call_internal_p (stmt, IFN_ASSUME))
+             && gimple_call_internal_p (stmt))
            {
-             gsi_remove (&i, true);
+             if (!fold_stmt (&i))
+               {
+                 gsi_next (&i);
+                 continue;
+               }
+             if (dump_file && (dump_flags & TDF_DETAILS))
+               {
+                 fprintf (dump_file, "Simplified\n  ");
+                 print_gimple_stmt (dump_file, stmt, 0, dump_flags);
+               }
+
+             old_stmt = stmt;
+             stmt = gsi_stmt (i);
+             update_stmt (stmt);
+
+             if (maybe_clean_or_replace_eh_stmt (old_stmt, stmt)
+                 && gimple_purge_dead_eh_edges (bb))
+               cfg_changed = true;
+
+             if (dump_file && (dump_flags & TDF_DETAILS))
+               {
+                 fprintf (dump_file, "to\n  ");
+                 print_gimple_stmt (dump_file, stmt, 0, dump_flags);
+                 fprintf (dump_file, "\n");
+               }
+             gsi_next (&i);
              continue;
            }
          if (!callee || !fndecl_built_in_p (callee, BUILT_IN_NORMAL))
@@ -4298,13 +4236,6 @@ pass_fold_builtins::execute (function *fun)
              switch (DECL_FUNCTION_CODE (callee))
                {
 
-               case BUILT_IN_STACK_RESTORE:
-                 result = optimize_stack_restore (i);
-                 if (result)
-                   break;
-                 gsi_next (&i);
-                 continue;
-
                case BUILT_IN_UNREACHABLE:
                  if (optimize_unreachable (i))
                    cfg_changed = true;
diff --git a/gcc/tree-ssa-forwprop.cc b/gcc/tree-ssa-forwprop.cc
index 917f3d90f6c..e0c93237b97 100644
--- a/gcc/tree-ssa-forwprop.cc
+++ b/gcc/tree-ssa-forwprop.cc
@@ -2132,6 +2132,115 @@ simplify_builtin_memcpy_memset (gimple_stmt_iterator 
*gsi_p, gcall *stmt2)
     }
 }
 
+
+/* Try to optimize out __builtin_stack_restore.  Optimize it out
+   if there is another __builtin_stack_restore in the same basic
+   block and no calls or ASM_EXPRs are in between, or if this block's
+   only outgoing edge is to EXIT_BLOCK and there are no calls or
+   ASM_EXPRs after this __builtin_stack_restore.
+   Note restore right before a noreturn function is not needed.
+   And skip some cheap calls that will most likely become an instruction.
+   Restoring the stack before a call is important to be able to keep
+   stack usage down so that call does not run out of stack.  */
+
+static bool
+optimize_stack_restore (gimple_stmt_iterator *gsi, gimple *call)
+{
+  if (!(cfun->curr_properties & PROP_last_full_fold))
+    return false;
+  tree callee;
+  gimple *stmt;
+
+  basic_block bb = gsi_bb (*gsi);
+
+  if (gimple_call_num_args (call) != 1
+      || TREE_CODE (gimple_call_arg (call, 0)) != SSA_NAME
+      || !POINTER_TYPE_P (TREE_TYPE (gimple_call_arg (call, 0))))
+    return false;
+
+  gimple_stmt_iterator i = *gsi;
+  for (gsi_next (&i); !gsi_end_p (i); gsi_next (&i))
+    {
+      stmt = gsi_stmt (i);
+      if (is_a<gasm*> (stmt))
+       return false;
+      gcall *call = dyn_cast<gcall*>(stmt);
+      if (!call)
+       continue;
+
+      /* We can remove the restore in front of noreturn
+        calls.  Since the restore will happen either
+        via an unwind/longjmp or not at all. */
+      if (gimple_call_noreturn_p (call))
+       break;
+
+      /* Internal calls are ok, to bypass
+        check first since fndecl will be null. */
+      if (gimple_call_internal_p (call))
+       continue;
+
+      callee = gimple_call_fndecl (call);
+      /* Non-builtin calls are not ok. */
+      if (!callee
+         || !fndecl_built_in_p (callee))
+       return false;
+
+      /* Do not remove stack updates before strub leave.  */
+      if (fndecl_built_in_p (callee, BUILT_IN___STRUB_LEAVE)
+         /* Alloca calls are not ok either. */
+         || fndecl_builtin_alloc_p (callee))
+       return false;
+
+      if (fndecl_built_in_p (callee, BUILT_IN_STACK_RESTORE))
+       goto second_stack_restore;
+
+      /* If not a simple or inexpensive builtin, then it is not ok either. */
+      if (!is_simple_builtin (callee)
+         && !is_inexpensive_builtin (callee))
+       return false;
+    }
+
+  /* Allow one successor of the exit block, or zero successors.  */
+  switch (EDGE_COUNT (bb->succs))
+    {
+    case 0:
+      break;
+    case 1:
+      if (single_succ_edge (bb)->dest != EXIT_BLOCK_PTR_FOR_FN (cfun))
+       return false;
+      break;
+    default:
+      return false;
+    }
+ second_stack_restore:
+
+  /* If there's exactly one use, then zap the call to __builtin_stack_save.
+     If there are multiple uses, then the last one should remove the call.
+     In any case, whether the call to __builtin_stack_save can be removed
+     or not is irrelevant to removing the call to __builtin_stack_restore.  */
+  if (has_single_use (gimple_call_arg (call, 0)))
+    {
+      gimple *stack_save = SSA_NAME_DEF_STMT (gimple_call_arg (call, 0));
+      if (is_gimple_call (stack_save))
+       {
+         callee = gimple_call_fndecl (stack_save);
+         if (callee && fndecl_built_in_p (callee, BUILT_IN_STACK_SAVE))
+           {
+             gimple_stmt_iterator stack_save_gsi;
+             tree rhs;
+
+             stack_save_gsi = gsi_for_stmt (stack_save);
+             rhs = build_int_cst (TREE_TYPE (gimple_call_arg (call, 0)), 0);
+             replace_call_with_value (&stack_save_gsi, rhs);
+           }
+       }
+    }
+
+  /* No effect, so the statement will be deleted.  */
+  replace_call_with_value (gsi, NULL_TREE);
+  return true;
+}
+
 /* *GSI_P is a GIMPLE_CALL to a builtin function.
    Optimize
    memcpy (p, "abcd", 4);
@@ -2163,6 +2272,8 @@ simplify_builtin_call (gimple_stmt_iterator *gsi_p, tree 
callee2, bool full_walk
 
   switch (DECL_FUNCTION_CODE (callee2))
     {
+    case BUILT_IN_STACK_RESTORE:
+      return optimize_stack_restore (gsi_p, as_a<gcall*>(stmt2));
     case BUILT_IN_MEMCMP:
     case BUILT_IN_MEMCMP_EQ:
       return simplify_builtin_memcmp (gsi_p, as_a<gcall*>(stmt2));
diff --git a/gcc/tree.h b/gcc/tree.h
index 4c8ad9842ff..6e46374357c 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -7005,6 +7005,15 @@ fndecl_built_in_p (const_tree node, built_in_function 
name1, F... names)
                                        name1, names...));
 }
 
+/* Returns true if the function decl NODE is an alloca. */
+inline bool
+fndecl_builtin_alloc_p (const_tree node)
+{
+  if (!fndecl_built_in_p (node, BUILT_IN_NORMAL))
+    return false;
+  return ALLOCA_FUNCTION_CODE_P (DECL_FUNCTION_CODE (node));
+}
+
 /* A struct for encapsulating location information about an operator
    and the operation built from it.
 
-- 
2.43.0

Reply via email to