Tighten check for whether sibcall references local variables
authorRichard Sandiford <richard.sandiford@arm.com>
Fri, 25 Nov 2016 08:17:46 +0000 (08:17 +0000)
committerRichard Sandiford <rsandifo@gcc.gnu.org>
Fri, 25 Nov 2016 08:17:46 +0000 (08:17 +0000)
This loop:

      /* Make sure the tail invocation of this function does not refer
         to local variables.  */
      FOR_EACH_LOCAL_DECL (cfun, idx, var)
        {
          if (TREE_CODE (var) != PARM_DECL
              && auto_var_in_fn_p (var, cfun->decl)
              && (ref_maybe_used_by_stmt_p (call, var)
                  || call_may_clobber_ref_p (call, var)))
            return;
        }

triggered even for local variables that are passed by value.
This meant that we didn't allow local aggregates to be passed
to a sibling call but did (for example) allow global aggregates
to be passed.

I think the loop is really checking for indirect references,
so should be able to skip any variables that never have their
address taken.

gcc/
* tree-tailcall.c (find_tail_calls): Allow calls to reference
local variables if all references are known to be direct.

gcc/testsuite/
* gcc.dg/tree-ssa/tailcall-8.c: New test.

From-SVN: r242860

gcc/ChangeLog
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.dg/tree-ssa/tailcall-8.c [new file with mode: 0644]
gcc/tree-tailcall.c

index f2b48289d6b9ee97aba875f96eeafd4d801746de..efc20162e12360e326ef0ef942b38ebeedd5d140 100644 (file)
@@ -1,3 +1,8 @@
+2016-11-25  Richard Sandiford  <richard.sandiford@arm.com>
+
+       * tree-tailcall.c (find_tail_calls): Allow calls to reference
+       local variables if all references are known to be direct.
+
 2016-11-25  Jakub Jelinek  <jakub@redhat.com>
            Prathamesh Kulkarni  <prathamesh.kulkarni@linaro.org>
 
index 1028191731fc7975e957ab4b686426e5a2f2b01f..e18397f2a4ccc5110b8c93abaa2719e961de2371 100644 (file)
@@ -1,3 +1,7 @@
+2016-11-25  Richard Sandiford  <richard.sandiford@arm.com>
+
+       * gcc.dg/tree-ssa/tailcall-8.c: New test.
+
 2016-11-25  Senthil Kumar Selvaraj  <senthil_kumar.selvaraj@atmel.com>
 
        * gcc.dg/pr64277.c: Use __INT32_TYPE__ for targets
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/tailcall-8.c b/gcc/testsuite/gcc.dg/tree-ssa/tailcall-8.c
new file mode 100644 (file)
index 0000000..ffeabe5
--- /dev/null
@@ -0,0 +1,80 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-tailc-details" } */
+
+struct s { int x; };
+void f_direct (struct s);
+void f_indirect (struct s *);
+void f_void (void);
+
+/* Tail call.  */
+void
+g1 (struct s param)
+{
+  f_direct (param);
+}
+
+/* Tail call.  */
+void
+g2 (struct s *param_ptr)
+{
+  f_direct (*param_ptr);
+}
+
+/* Tail call.  */
+void
+g3 (struct s *param_ptr)
+{
+  f_indirect (param_ptr);
+}
+
+/* Tail call.  */
+void
+g4 (struct s *param_ptr)
+{
+  f_indirect (param_ptr);
+  f_void ();
+}
+
+/* Tail call.  */
+void
+g5 (struct s param)
+{
+  struct s local = param;
+  f_direct (local);
+}
+
+/* Tail call.  */
+void
+g6 (struct s param)
+{
+  struct s local = param;
+  f_direct (local);
+  f_void ();
+}
+
+/* Not a tail call.  */
+void
+g7 (struct s param)
+{
+  struct s local = param;
+  f_indirect (&local);
+}
+
+/* Not a tail call.  */
+void
+g8 (struct s *param_ptr)
+{
+  struct s local = *param_ptr;
+  f_indirect (&local);
+}
+
+/* Not a tail call.  */
+void
+g9 (struct s *param_ptr)
+{
+  struct s local = *param_ptr;
+  f_indirect (&local);
+  f_void ();
+}
+
+/* { dg-final { scan-tree-dump-times "Found tail call" 6 "tailc" } } */
index f97541d35a544ab83ebe0de5aa13fe419e614e71..66a0a4c446097f61dc47e66ec61079d836833efb 100644 (file)
@@ -504,12 +504,14 @@ find_tail_calls (basic_block bb, struct tailcall **ret)
        tail_recursion = true;
     }
 
-  /* Make sure the tail invocation of this function does not refer
-     to local variables.  */
+  /* Make sure the tail invocation of this function does not indirectly
+     refer to local variables.  (Passing variables directly by value
+     is OK.)  */
   FOR_EACH_LOCAL_DECL (cfun, idx, var)
     {
       if (TREE_CODE (var) != PARM_DECL
          && auto_var_in_fn_p (var, cfun->decl)
+         && may_be_aliased (var)
          && (ref_maybe_used_by_stmt_p (call, var)
              || call_may_clobber_ref_p (call, var)))
        return;