diff mbox

Optimize callers using nonnull attribute

Message ID alpine.DEB.2.10.1310071517260.21427@stedding.saclay.inria.fr
State New
Headers show

Commit Message

Marc Glisse Oct. 7, 2013, 1:52 p.m. UTC
On Mon, 7 Oct 2013, Richard Biener wrote:

> On Mon, Oct 7, 2013 at 12:33 AM, Marc Glisse <marc.glisse@inria.fr> wrote:
>> Hello,
>>
>> this patch asserts that when we call a function with the nonnull attribute,
>> the corresponding argument is not zero, just like when we dereference a
>> pointer. Everything is under a check for flag_delete_null_pointer_checks.
>>
>> Note that this function currently gives up if the statement may throw
>> (because in some languages indirections may throw?), but this could probably
>> be relaxed a bit so my example would still work when compiled with g++,
>> without having to mark f1 and f2 as throw().
>>
>> Bootstrap (default languages) + testsuite on x86_64-unknown-linux-gnu.
>
> Can you please restructure it in a way to not require a goto?  That is,
> move searching for a non-null opportunity into a helper function?

Thanks. I wasn't sure what to put in the helper and what to keep in the 
original function, so I'll wait a bit for comments before the commit.

Bootstrap (default languages) + testsuite on x86_64-unknown-linux-gnu.

2013-10-08  Marc Glisse  <marc.glisse@inria.fr>

 	PR tree-optimization/58480
gcc/
 	* tree-vrp.c (infer_nonnull_range): New function.
 	(infer_value_range): Call infer_nonnull_range.

gcc/testsuite/
 	* gcc.dg/tree-ssa/pr58480.c: New file.

Comments

Jeff Law Oct. 8, 2013, 4:33 a.m. UTC | #1
On 10/07/13 07:52, Marc Glisse wrote:
> On Mon, 7 Oct 2013, Richard Biener wrote:
>
>> On Mon, Oct 7, 2013 at 12:33 AM, Marc Glisse <marc.glisse@inria.fr>
>> wrote:
>>> Hello,
>>>
>>> this patch asserts that when we call a function with the nonnull
>>> attribute,
>>> the corresponding argument is not zero, just like when we dereference a
>>> pointer. Everything is under a check for
>>> flag_delete_null_pointer_checks.
>>>
>>> Note that this function currently gives up if the statement may throw
>>> (because in some languages indirections may throw?), but this could
>>> probably
>>> be relaxed a bit so my example would still work when compiled with g++,
>>> without having to mark f1 and f2 as throw().
>>>
>>> Bootstrap (default languages) + testsuite on x86_64-unknown-linux-gnu.
>>
>> Can you please restructure it in a way to not require a goto?  That is,
>> move searching for a non-null opportunity into a helper function?
>
> Thanks. I wasn't sure what to put in the helper and what to keep in the
> original function, so I'll wait a bit for comments before the commit.
>
> Bootstrap (default languages) + testsuite on x86_64-unknown-linux-gnu.
I like it.  Tweak per Richi's suggestion to avoid the goto and get it 
committed!

jeff
Richard Biener Oct. 8, 2013, 10:01 a.m. UTC | #2
On Mon, Oct 7, 2013 at 3:52 PM, Marc Glisse <marc.glisse@inria.fr> wrote:
> On Mon, 7 Oct 2013, Richard Biener wrote:
>
>> On Mon, Oct 7, 2013 at 12:33 AM, Marc Glisse <marc.glisse@inria.fr> wrote:
>>>
>>> Hello,
>>>
>>> this patch asserts that when we call a function with the nonnull
>>> attribute,
>>> the corresponding argument is not zero, just like when we dereference a
>>> pointer. Everything is under a check for flag_delete_null_pointer_checks.
>>>
>>> Note that this function currently gives up if the statement may throw
>>> (because in some languages indirections may throw?), but this could
>>> probably
>>> be relaxed a bit so my example would still work when compiled with g++,
>>> without having to mark f1 and f2 as throw().
>>>
>>> Bootstrap (default languages) + testsuite on x86_64-unknown-linux-gnu.
>>
>>
>> Can you please restructure it in a way to not require a goto?  That is,
>> move searching for a non-null opportunity into a helper function?
>
>
> Thanks. I wasn't sure what to put in the helper and what to keep in the
> original function, so I'll wait a bit for comments before the commit.

Works for me.

Richard.

>
> Bootstrap (default languages) + testsuite on x86_64-unknown-linux-gnu.
>
> 2013-10-08  Marc Glisse  <marc.glisse@inria.fr>
>
>         PR tree-optimization/58480
> gcc/
>         * tree-vrp.c (infer_nonnull_range): New function.
>         (infer_value_range): Call infer_nonnull_range.
>
>
> gcc/testsuite/
>         * gcc.dg/tree-ssa/pr58480.c: New file.
>
> --
> Marc Glisse
>
> Index: testsuite/gcc.dg/tree-ssa/pr58480.c
> ===================================================================
> --- testsuite/gcc.dg/tree-ssa/pr58480.c (revision 0)
> +++ testsuite/gcc.dg/tree-ssa/pr58480.c (working copy)
> @@ -0,0 +1,19 @@
> +/* { dg-do compile { target { ! keeps_null_pointer_checks } } } */
> +/* { dg-options "-O2 -fdump-tree-vrp1" } */
> +
> +extern void eliminate (void);
> +extern void* f1 (void *a, void *b) __attribute__((nonnull));
> +extern void* f2 (void *a, void *b) __attribute__((nonnull(2)));
> +void g1 (void*p, void*q){
> +  f1 (q, p);
> +  if (p == 0)
> +    eliminate ();
> +}
> +void g2 (void*p, void*q){
> +  f2 (q, p);
> +  if (p == 0)
> +    eliminate ();
> +}
> +
> +/* { dg-final { scan-tree-dump-times "Folding predicate\[^\\n\]*to 0" 2
> "vrp1" } } */
> +/* { dg-final { cleanup-tree-dump "vrp1" } } */
>
> Property changes on: testsuite/gcc.dg/tree-ssa/pr58480.c
> ___________________________________________________________________
> Added: svn:eol-style
> ## -0,0 +1 ##
> +native
> \ No newline at end of property
> Added: svn:keywords
> ## -0,0 +1 ##
> +Author Date Id Revision URL
> \ No newline at end of property
> Index: tree-vrp.c
> ===================================================================
> --- tree-vrp.c  (revision 203241)
> +++ tree-vrp.c  (working copy)
> @@ -4455,63 +4455,103 @@ build_assert_expr_for (tree cond, tree v
>
>  static inline bool
>  fp_predicate (gimple stmt)
>  {
>    GIMPLE_CHECK (stmt, GIMPLE_COND);
>
>    return FLOAT_TYPE_P (TREE_TYPE (gimple_cond_lhs (stmt)));
>  }
>
>
> +/* If OP can be inferred to be non-zero after STMT executes, return true.
> */
> +
> +static bool
> +infer_nonnull_range (gimple stmt, tree op)
> +{
> +  /* We can only assume that a pointer dereference will yield
> +     non-NULL if -fdelete-null-pointer-checks is enabled.  */
> +  if (!flag_delete_null_pointer_checks
> +      || !POINTER_TYPE_P (TREE_TYPE (op))
> +      || gimple_code (stmt) == GIMPLE_ASM)
> +    return false;
> +
> +  unsigned num_uses, num_loads, num_stores;
> +
> +  count_uses_and_derefs (op, stmt, &num_uses, &num_loads, &num_stores);
> +  if (num_loads + num_stores > 0)
> +    return true;
> +
> +  if (gimple_code (stmt) == GIMPLE_CALL)
> +    {
> +      tree fntype = gimple_call_fntype (stmt);
> +      tree attrs = TYPE_ATTRIBUTES (fntype);
> +      for (; attrs; attrs = TREE_CHAIN (attrs))
> +       {
> +         attrs = lookup_attribute ("nonnull", attrs);
> +
> +         /* If "nonnull" wasn't specified, we know nothing about
> +            the argument.  */
> +         if (attrs == NULL_TREE)
> +           return false;
> +
> +         /* If "nonnull" applies to all the arguments, then ARG
> +            is non-null.  */
> +         if (TREE_VALUE (attrs) == NULL_TREE)
> +           return true;
> +
> +         /* Now see if op appears in the nonnull list.  */
> +         for (tree t = TREE_VALUE (attrs); t; t = TREE_CHAIN (t))
> +           {
> +             int idx = TREE_INT_CST_LOW (TREE_VALUE (t)) - 1;
> +             tree arg = gimple_call_arg (stmt, idx);
> +             if (op == arg)
> +               return true;
> +           }
> +       }
> +    }
> +
> +  return false;
> +}
> +
>  /* If the range of values taken by OP can be inferred after STMT executes,
>     return the comparison code (COMP_CODE_P) and value (VAL_P) that
>     describes the inferred range.  Return true if a range could be
>     inferred.  */
>
>  static bool
>  infer_value_range (gimple stmt, tree op, enum tree_code *comp_code_p, tree
> *val_p)
>  {
>    *val_p = NULL_TREE;
>    *comp_code_p = ERROR_MARK;
>
>    /* Do not attempt to infer anything in names that flow through
>       abnormal edges.  */
>    if (SSA_NAME_OCCURS_IN_ABNORMAL_PHI (op))
>      return false;
>
>    /* Similarly, don't infer anything from statements that may throw
> -     exceptions.  */
> +     exceptions. ??? Relax this requirement?  */
>    if (stmt_could_throw_p (stmt))
>      return false;
>
>    /* If STMT is the last statement of a basic block with no
>       successors, there is no point inferring anything about any of its
>       operands.  We would not be able to find a proper insertion point
>       for the assertion, anyway.  */
>    if (stmt_ends_bb_p (stmt) && EDGE_COUNT (gimple_bb (stmt)->succs) == 0)
>      return false;
>
> -  /* We can only assume that a pointer dereference will yield
> -     non-NULL if -fdelete-null-pointer-checks is enabled.  */
> -  if (flag_delete_null_pointer_checks
> -      && POINTER_TYPE_P (TREE_TYPE (op))
> -      && gimple_code (stmt) != GIMPLE_ASM)
> +  if (infer_nonnull_range (stmt, op))
>      {
> -      unsigned num_uses, num_loads, num_stores;
> -
> -      count_uses_and_derefs (op, stmt, &num_uses, &num_loads, &num_stores);
> -      if (num_loads + num_stores > 0)
> -       {
> -         *val_p = build_int_cst (TREE_TYPE (op), 0);
> -         *comp_code_p = NE_EXPR;
> -         return true;
> -       }
> +      *val_p = build_int_cst (TREE_TYPE (op), 0);
> +      *comp_code_p = NE_EXPR;
> +      return true;
>      }
>
>    return false;
>  }
>
>
>  void dump_asserts_for (FILE *, tree);
>  void debug_asserts_for (tree);
>  void dump_all_asserts (FILE *);
>  void debug_all_asserts (void);
>
diff mbox

Patch

Index: testsuite/gcc.dg/tree-ssa/pr58480.c
===================================================================
--- testsuite/gcc.dg/tree-ssa/pr58480.c	(revision 0)
+++ testsuite/gcc.dg/tree-ssa/pr58480.c	(working copy)
@@ -0,0 +1,19 @@ 
+/* { dg-do compile { target { ! keeps_null_pointer_checks } } } */
+/* { dg-options "-O2 -fdump-tree-vrp1" } */
+
+extern void eliminate (void);
+extern void* f1 (void *a, void *b) __attribute__((nonnull));
+extern void* f2 (void *a, void *b) __attribute__((nonnull(2)));
+void g1 (void*p, void*q){
+  f1 (q, p);
+  if (p == 0)
+    eliminate ();
+}
+void g2 (void*p, void*q){
+  f2 (q, p);
+  if (p == 0)
+    eliminate ();
+}
+
+/* { dg-final { scan-tree-dump-times "Folding predicate\[^\\n\]*to 0" 2 "vrp1" } } */
+/* { dg-final { cleanup-tree-dump "vrp1" } } */

Property changes on: testsuite/gcc.dg/tree-ssa/pr58480.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+Author Date Id Revision URL
\ No newline at end of property
Index: tree-vrp.c
===================================================================
--- tree-vrp.c	(revision 203241)
+++ tree-vrp.c	(working copy)
@@ -4455,63 +4455,103 @@  build_assert_expr_for (tree cond, tree v
 
 static inline bool
 fp_predicate (gimple stmt)
 {
   GIMPLE_CHECK (stmt, GIMPLE_COND);
 
   return FLOAT_TYPE_P (TREE_TYPE (gimple_cond_lhs (stmt)));
 }
 
 
+/* If OP can be inferred to be non-zero after STMT executes, return true.  */
+
+static bool
+infer_nonnull_range (gimple stmt, tree op)
+{
+  /* We can only assume that a pointer dereference will yield
+     non-NULL if -fdelete-null-pointer-checks is enabled.  */
+  if (!flag_delete_null_pointer_checks
+      || !POINTER_TYPE_P (TREE_TYPE (op))
+      || gimple_code (stmt) == GIMPLE_ASM)
+    return false;
+
+  unsigned num_uses, num_loads, num_stores;
+
+  count_uses_and_derefs (op, stmt, &num_uses, &num_loads, &num_stores);
+  if (num_loads + num_stores > 0)
+    return true;
+
+  if (gimple_code (stmt) == GIMPLE_CALL)
+    {
+      tree fntype = gimple_call_fntype (stmt);
+      tree attrs = TYPE_ATTRIBUTES (fntype);
+      for (; attrs; attrs = TREE_CHAIN (attrs))
+	{
+	  attrs = lookup_attribute ("nonnull", attrs);
+
+	  /* If "nonnull" wasn't specified, we know nothing about
+	     the argument.  */
+	  if (attrs == NULL_TREE)
+	    return false;
+
+	  /* If "nonnull" applies to all the arguments, then ARG
+	     is non-null.  */
+	  if (TREE_VALUE (attrs) == NULL_TREE)
+	    return true;
+
+	  /* Now see if op appears in the nonnull list.  */
+	  for (tree t = TREE_VALUE (attrs); t; t = TREE_CHAIN (t))
+	    {
+	      int idx = TREE_INT_CST_LOW (TREE_VALUE (t)) - 1;
+	      tree arg = gimple_call_arg (stmt, idx);
+	      if (op == arg)
+		return true;
+	    }
+	}
+    }
+
+  return false;
+}
+
 /* If the range of values taken by OP can be inferred after STMT executes,
    return the comparison code (COMP_CODE_P) and value (VAL_P) that
    describes the inferred range.  Return true if a range could be
    inferred.  */
 
 static bool
 infer_value_range (gimple stmt, tree op, enum tree_code *comp_code_p, tree *val_p)
 {
   *val_p = NULL_TREE;
   *comp_code_p = ERROR_MARK;
 
   /* Do not attempt to infer anything in names that flow through
      abnormal edges.  */
   if (SSA_NAME_OCCURS_IN_ABNORMAL_PHI (op))
     return false;
 
   /* Similarly, don't infer anything from statements that may throw
-     exceptions.  */
+     exceptions. ??? Relax this requirement?  */
   if (stmt_could_throw_p (stmt))
     return false;
 
   /* If STMT is the last statement of a basic block with no
      successors, there is no point inferring anything about any of its
      operands.  We would not be able to find a proper insertion point
      for the assertion, anyway.  */
   if (stmt_ends_bb_p (stmt) && EDGE_COUNT (gimple_bb (stmt)->succs) == 0)
     return false;
 
-  /* We can only assume that a pointer dereference will yield
-     non-NULL if -fdelete-null-pointer-checks is enabled.  */
-  if (flag_delete_null_pointer_checks
-      && POINTER_TYPE_P (TREE_TYPE (op))
-      && gimple_code (stmt) != GIMPLE_ASM)
+  if (infer_nonnull_range (stmt, op))
     {
-      unsigned num_uses, num_loads, num_stores;
-
-      count_uses_and_derefs (op, stmt, &num_uses, &num_loads, &num_stores);
-      if (num_loads + num_stores > 0)
-	{
-	  *val_p = build_int_cst (TREE_TYPE (op), 0);
-	  *comp_code_p = NE_EXPR;
-	  return true;
-	}
+      *val_p = build_int_cst (TREE_TYPE (op), 0);
+      *comp_code_p = NE_EXPR;
+      return true;
     }
 
   return false;
 }
 
 
 void dump_asserts_for (FILE *, tree);
 void debug_asserts_for (tree);
 void dump_all_asserts (FILE *);
 void debug_all_asserts (void);