Re: Tighten check for whether a sibcall references local variables

2016-11-23 Thread Jeff Law

On 11/23/2016 10:09 AM, Richard Sandiford wrote:

Richard Biener  writes:

On Tue, Nov 22, 2016 at 10:00 AM, Richard Sandiford
 wrote:

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.

Tested on aarch64-linux-gnu and x86_64-linux-gnu.  OK to install?


Ok.  We've had various correctness issues in this part in the past so
I'd prefer if you can rewrite your dg-do compile tests to dg-do run ones
that verify the code works correctly.  I suggest to use a dg-additional-sources
with a separate TU for the execution driver.


OK, how's this?  Tested on aarch64-linux-gnu and x86_64-linux-gnu.

Thanks,
Richard


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

Works for me.
jeff



Re: Tighten check for whether a sibcall references local variables

2016-11-23 Thread Richard Sandiford
Richard Biener  writes:
> On Tue, Nov 22, 2016 at 10:00 AM, Richard Sandiford
>  wrote:
>> 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.
>>
>> Tested on aarch64-linux-gnu and x86_64-linux-gnu.  OK to install?
>
> Ok.  We've had various correctness issues in this part in the past so
> I'd prefer if you can rewrite your dg-do compile tests to dg-do run ones
> that verify the code works correctly.  I suggest to use a 
> dg-additional-sources
> with a separate TU for the execution driver.

OK, how's this?  Tested on aarch64-linux-gnu and x86_64-linux-gnu.

Thanks,
Richard


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

diff --git a/gcc/testsuite/gcc.dg/tree-ssa/tailcall-7-run.c 
b/gcc/testsuite/gcc.dg/tree-ssa/tailcall-7-run.c
new file mode 100644
index 000..d18ff9d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/tailcall-7-run.c
@@ -0,0 +1,72 @@
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+/* { dg-additional-sources "tailcall-7.c" } */
+
+struct s { int x; };
+extern struct s global;
+
+void g1 (void);
+void g2 (void);
+void g3 (struct s *);
+struct s g4 (struct s);
+struct s g5 (void);
+struct s g6 (void);
+struct s g7 (void);
+struct s g8 (struct s *);
+int g9 (struct s);
+int g10 (int);
+
+struct s last;
+struct s tmp;
+
+struct s
+f (int i)
+{
+  struct s ret;
+  ret.x = i + 100;
+  last = ret;
+  return ret;
+}
+
+void
+callit (void (*fn) (void))
+{
+  fn ();
+}
+
+int
+test (int last_val, int global_val, int tmp_val)
+{
+  return last.x == last_val && global.x == global_val && tmp.x == tmp_val;
+}
+
+int
+main (void)
+{
+  global.x = 200;
+  tmp.x = 300;
+  g1 ();
+  if (!test (101, 200, 300))
+__builtin_abort ();
+  g2 ();
+  if (!test (102, 102, 300))
+__builtin_abort ();
+  g3 (&tmp);
+  if (!test (103, 102, 103))
+__builtin_abort ();
+  if (g4 (tmp).x != 104 || !test (104, 102, 103))
+__builtin_abort ();
+  if (g5 ().x != 105 || !test (105, 102, 103))
+__builtin_abort ();
+  if (g6 ().x != 106 || !test (106, 102, 103))
+__builtin_abort ();
+  if (g7 ().x != 107 || !test (107, 107, 103))
+__builtin_abort ();
+  if (g8 (&tmp).x != 108 || !test (108, 107, 108))
+__builtin_abort ();
+  if (g9 (tmp) != 9 || !test (109, 107, 108))
+__builtin_abort ();
+  if (g10 (10) != 10 || !test (110, 107, 108))
+__builtin_abort ();
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/tailcall-8-run.c 
b/gcc/testsuite/gcc.dg/tree-ssa/tailcall-8-run.c
new file mode 100644
index 000..f892909
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/tailcall-8-run.c
@@ -0,0 +1,86 @@
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+/* { dg-additional-sources "tailcall-8.c" } */
+
+struct s { int x; };
+
+int expected;
+struct s *last_ptr;
+struct s tmp;
+
+void
+start (int val, struct s *initial_last_ptr)
+{
+  expected = val;
+  tmp.x = val;
+  last_ptr = initial_last_ptr;
+}
+
+void
+f_direct (struct s param)
+{
+  if (param.x != expected)
+__builtin_abort ();
+}
+
+void
+f_indirect (struct s *ptr)
+{
+  if (ptr->x != expected)
+__builtin_abort ();
+  last_ptr = ptr;
+  ptr->x += 100;
+}
+
+void
+f_void (void)
+{
+  if (last_ptr->x != expected + 100)
+__builtin_abort ();
+}
+
+
+void g1 (struct s);
+void g2 (struct s *);
+void g3 (struct s *);
+void g4 (struct s *);
+void g5 (struct s);
+void g6 (struct s);
+void g7 (struct s);
+void g8 (struct s *);
+void g9 (struct s *);
+
+int
+main (void)
+{
+  struct s g6_s = { 106 };
+
+  start (1, 0);
+  g1 (tmp);
+
+  start (2, 0);
+  g2 (&tmp);
+
+  start (3, 0);
+  g3 (&tmp);
+
+  start (4, 0);
+  g4 (&tmp);
+
+  start (5, 0);
+  g5 (tmp);
+
+  start (6, &g6_s);
+  g6 (tmp);
+
+  start (7, 0);
+  g7 (tmp);
+
+  start (8, 0);
+  g8 (&tmp);
+
+  start (9, 0);
+  g9 (&tmp);
+
+  return 0;
+}



Re: Tighten check for whether a sibcall references local variables

2016-11-22 Thread Richard Biener
On Tue, Nov 22, 2016 at 10:00 AM, Richard Sandiford
 wrote:
> 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.
>
> Tested on aarch64-linux-gnu and x86_64-linux-gnu.  OK to install?

Ok.  We've had various correctness issues in this part in the past so
I'd prefer if you can rewrite your dg-do compile tests to dg-do run ones
that verify the code works correctly.  I suggest to use a dg-additional-sources
with a separate TU for the execution driver.

Thanks,
Richard.

> Thanks,
> Richard
>
>
> 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.
>
> 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
> index 000..ffeabe5
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/tree-ssa/tailcall-8.c
> @@ -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" } } */
> diff --git a/gcc/tree-tailcall.c b/gcc/tree-tailcall.c
> index f97541d..66a0a4c 100644
> --- a/gcc/tree-tailcall.c
> +++ b/gcc/tree-tailcall.c
> @@ -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;
>


Tighten check for whether a sibcall references local variables

2016-11-22 Thread Richard Sandiford
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.

Tested on aarch64-linux-gnu and x86_64-linux-gnu.  OK to install?

Thanks,
Richard


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.

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
index 000..ffeabe5
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/tailcall-8.c
@@ -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" } } */
diff --git a/gcc/tree-tailcall.c b/gcc/tree-tailcall.c
index f97541d..66a0a4c 100644
--- a/gcc/tree-tailcall.c
+++ b/gcc/tree-tailcall.c
@@ -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;