Hello,

this patch adds an attribute to let the compiler know that a function never returns NULL. I saw some ECF_* flags, but the attribute seems sufficient. I considered using nonnull(0), but then it would have been confusing that the version of nonnull without arguments applies only to parameters and not the return value.

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

        PR tree-optimization/20318
gcc/c-family/
        * c-common.c (handle_returns_nonnull_attribute): New function.
        (c_common_attribute_table): Add returns_nonnull.

gcc/
        * doc/extend.texi (returns_nonnull): New function attribute.
        * fold-const.c (tree_expr_nonzero_warnv_p): Look for returns_nonnull
        attribute.
        * tree-vrp.c (gimple_stmt_nonzero_warnv_p): Likewise.
        (stmt_interesting_for_vrp): Accept all GIMPLE_CALL.

gcc/testsuite/
        * c-c++-common/pr20318.c: New file.
        * gcc.dg/tree-ssa/pr20318.c: New file.

--
Marc Glisse
Index: c-family/c-common.c
===================================================================
--- c-family/c-common.c (revision 203241)
+++ c-family/c-common.c (working copy)
@@ -364,20 +364,21 @@ static tree handle_warn_unused_result_at
                                                 bool *);
 static tree handle_sentinel_attribute (tree *, tree, tree, int, bool *);
 static tree handle_type_generic_attribute (tree *, tree, tree, int, bool *);
 static tree handle_alloc_size_attribute (tree *, tree, tree, int, bool *);
 static tree handle_target_attribute (tree *, tree, tree, int, bool *);
 static tree handle_optimize_attribute (tree *, tree, tree, int, bool *);
 static tree ignore_attribute (tree *, tree, tree, int, bool *);
 static tree handle_no_split_stack_attribute (tree *, tree, tree, int, bool *);
 static tree handle_fnspec_attribute (tree *, tree, tree, int, bool *);
 static tree handle_warn_unused_attribute (tree *, tree, tree, int, bool *);
+static tree handle_returns_nonnull_attribute (tree *, tree, tree, int, bool *);
 
 static void check_function_nonnull (tree, int, tree *);
 static void check_nonnull_arg (void *, tree, unsigned HOST_WIDE_INT);
 static bool nonnull_check_p (tree, unsigned HOST_WIDE_INT);
 static bool get_nonnull_operand (tree, unsigned HOST_WIDE_INT *);
 static int resort_field_decl_cmp (const void *, const void *);
 
 /* Reserved words.  The third field is a mask: keywords are disabled
    if they match the mask.
 
@@ -740,20 +741,22 @@ const struct attribute_spec c_common_att
   { "*tm regparm",            0, 0, false, true, true,
                              ignore_attribute, false },
   { "no_split_stack",        0, 0, true,  false, false,
                              handle_no_split_stack_attribute, false },
   /* For internal use (marking of builtins and runtime functions) only.
      The name contains space to prevent its usage in source code.  */
   { "fn spec",               1, 1, false, true, true,
                              handle_fnspec_attribute, false },
   { "warn_unused",            0, 0, false, false, false,
                              handle_warn_unused_attribute, false },
+  { "returns_nonnull",        0, 0, false, true, true,
+                             handle_returns_nonnull_attribute, false },
   { NULL,                     0, 0, false, false, false, NULL, false }
 };
 
 /* Give the specifications for the format attributes, used by C and all
    descendants.  */
 
 const struct attribute_spec c_common_format_attribute_table[] =
 {
   /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler,
        affects_type_identity } */
@@ -9041,20 +9044,37 @@ handle_no_split_stack_attribute (tree *n
     }
   else if (DECL_INITIAL (decl))
     {
       error_at (DECL_SOURCE_LOCATION (decl),
                "can%'t set %qE attribute after definition", name);
       *no_add_attrs = true;
     }
 
   return NULL_TREE;
 }
+
+/* Handle a "returns_nonnull" attribute; arguments as in
+   struct attribute_spec.handler.  */
+
+static tree
+handle_returns_nonnull_attribute (tree *node, tree, tree, int,
+                                 bool *no_add_attrs)
+{
+  // Even without a prototype we still have a return type we can check.
+  if (TREE_CODE (TREE_TYPE (*node)) != POINTER_TYPE)
+    {
+      error ("returns_nonnull attribute on a function not returning a 
pointer");
+      *no_add_attrs = true;
+    }
+  return NULL_TREE;
+}
+
 
 /* Check for valid arguments being passed to a function with FNTYPE.
    There are NARGS arguments in the array ARGARRAY.  */
 void
 check_function_arguments (const_tree fntype, int nargs, tree *argarray)
 {
   /* Check for null being passed in a pointer argument that must be
      non-null.  We also need to do this if format checking is enabled.  */
 
   if (warn_nonnull)
Index: doc/extend.texi
===================================================================
--- doc/extend.texi     (revision 203241)
+++ doc/extend.texi     (working copy)
@@ -2126,21 +2126,22 @@ attributes when making a declaration.  T
 attribute specification inside double parentheses.  The following
 attributes are currently defined for functions on all targets:
 @code{aligned}, @code{alloc_size}, @code{noreturn},
 @code{returns_twice}, @code{noinline}, @code{noclone},
 @code{always_inline}, @code{flatten}, @code{pure}, @code{const},
 @code{nothrow}, @code{sentinel}, @code{format}, @code{format_arg},
 @code{no_instrument_function}, @code{no_split_stack},
 @code{section}, @code{constructor},
 @code{destructor}, @code{used}, @code{unused}, @code{deprecated},
 @code{weak}, @code{malloc}, @code{alias}, @code{ifunc},
-@code{warn_unused_result}, @code{nonnull}, @code{gnu_inline},
+@code{warn_unused_result}, @code{nonnull},
+@code{returns_nonnull}, @code{gnu_inline},
 @code{externally_visible}, @code{hot}, @code{cold}, @code{artificial},
 @code{no_sanitize_address}, @code{no_address_safety_analysis},
 @code{no_sanitize_undefined},
 @code{error} and @code{warning}.
 Several other attributes are defined for functions on particular
 target systems.  Other attributes, including @code{section} are
 supported for variables declarations (@pxref{Variable Attributes})
 and for types (@pxref{Type Attributes}).
 
 GCC plugins may provide their own attributes.
@@ -3302,20 +3303,34 @@ on the knowledge that certain function a
 If no argument index list is given to the @code{nonnull} attribute,
 all pointer arguments are marked as non-null.  To illustrate, the
 following declaration is equivalent to the previous example:
 
 @smallexample
 extern void *
 my_memcpy (void *dest, const void *src, size_t len)
         __attribute__((nonnull));
 @end smallexample
 
+@item returns_nonnull (@var{arg-index}, @dots{})
+@cindex @code{returns_nonnull} function attribute
+The @code{returns_nonnull} attribute specifies that the function
+return value should be a non-null pointer.  For instance, the declaration:
+
+@smallexample
+extern void *
+mymalloc (size_t len) __attribute__((returns_nonnull));
+@end smallexample
+
+@noindent
+lets the compiler optimize callers based on the knowledge
+that the return value will never be null.
+
 @item noreturn
 @cindex @code{noreturn} function attribute
 A few standard library functions, such as @code{abort} and @code{exit},
 cannot return.  GCC knows this automatically.  Some programs define
 their own functions that never return.  You can declare them
 @code{noreturn} to tell the compiler this fact.  For example,
 
 @smallexample
 @group
 void fatal () __attribute__ ((noreturn));
Index: fold-const.c
===================================================================
--- fold-const.c        (revision 203241)
+++ fold-const.c        (working copy)
@@ -16222,20 +16222,24 @@ tree_expr_nonzero_warnv_p (tree t, bool
                                        strict_overflow_p);
 
     case CALL_EXPR:
       {
        tree fndecl = get_callee_fndecl (t);
        if (!fndecl) return false;
        if (flag_delete_null_pointer_checks && !flag_check_new
            && DECL_IS_OPERATOR_NEW (fndecl)
            && !TREE_NOTHROW (fndecl))
          return true;
+       if (flag_delete_null_pointer_checks
+           && lookup_attribute ("returns_nonnull",
+                TYPE_ATTRIBUTES (TREE_TYPE (fndecl))))
+         return true;
        return alloca_call_p (t);
       }
 
     default:
       break;
     }
   return false;
 }
 
 /* Return true when T is an address and is known to be nonzero.
Index: testsuite/c-c++-common/pr20318.c
===================================================================
--- testsuite/c-c++-common/pr20318.c    (revision 0)
+++ testsuite/c-c++-common/pr20318.c    (working copy)
@@ -0,0 +1,3 @@
+/* { dg-do compile } */
+
+extern int f() __attribute__((returns_nonnull)); /* { dg-error "not returning 
a pointer" } */

Property changes on: testsuite/c-c++-common/pr20318.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: testsuite/gcc.dg/tree-ssa/pr20318.c
===================================================================
--- testsuite/gcc.dg/tree-ssa/pr20318.c (revision 0)
+++ testsuite/gcc.dg/tree-ssa/pr20318.c (working copy)
@@ -0,0 +1,19 @@
+/* { dg-do compile { target { ! keeps_null_pointer_checks } } } */
+/* { dg-options "-O2 -fdump-tree-original -fdump-tree-vrp1" } */
+
+extern int* f(int) __attribute__((returns_nonnull));
+extern void eliminate ();
+void g () {
+  if (f (2) == 0)
+    eliminate ();
+}
+void h () {
+  int *p = f (2);
+  if (p == 0)
+    eliminate ();
+}
+
+/* { dg-final { scan-tree-dump-times "== 0" 1 "original" } } */
+/* { dg-final { scan-tree-dump-times "Folding predicate\[^\\n\]*to 0" 1 "vrp1" 
} } */
+/* { dg-final { cleanup-tree-dump "original" } } */
+/* { dg-final { cleanup-tree-dump "vrp1" } } */

Property changes on: testsuite/gcc.dg/tree-ssa/pr20318.c
___________________________________________________________________
Added: svn:keywords
## -0,0 +1 ##
+Author Date Id Revision URL
\ No newline at end of property
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: tree-vrp.c
===================================================================
--- tree-vrp.c  (revision 203241)
+++ tree-vrp.c  (working copy)
@@ -1031,40 +1031,44 @@ gimple_assign_nonzero_warnv_p (gimple st
     case GIMPLE_SINGLE_RHS:
       return tree_single_nonzero_warnv_p (gimple_assign_rhs1 (stmt),
                                          strict_overflow_p);
     case GIMPLE_INVALID_RHS:
       gcc_unreachable ();
     default:
       gcc_unreachable ();
     }
 }
 
-/* Return true if STMT is know to to compute a non-zero value.
+/* Return true if STMT is known to compute a non-zero value.
    If the return value is based on the assumption that signed overflow is
    undefined, set *STRICT_OVERFLOW_P to true; otherwise, don't change
    *STRICT_OVERFLOW_P.*/
 
 static bool
 gimple_stmt_nonzero_warnv_p (gimple stmt, bool *strict_overflow_p)
 {
   switch (gimple_code (stmt))
     {
     case GIMPLE_ASSIGN:
       return gimple_assign_nonzero_warnv_p (stmt, strict_overflow_p);
     case GIMPLE_CALL:
       {
        tree fndecl = gimple_call_fndecl (stmt);
        if (!fndecl) return false;
        if (flag_delete_null_pointer_checks && !flag_check_new
            && DECL_IS_OPERATOR_NEW (fndecl)
            && !TREE_NOTHROW (fndecl))
          return true;
+       if (flag_delete_null_pointer_checks && 
+           lookup_attribute ("returns_nonnull",
+                             TYPE_ATTRIBUTES (gimple_call_fntype (stmt))))
+         return true;
        return gimple_alloca_call_p (stmt);
       }
     default:
       gcc_unreachable ();
     }
 }
 
 /* Like tree_expr_nonzero_warnv_p, but this function uses value ranges
    obtained so far.  */
 
@@ -6489,24 +6493,21 @@ stmt_interesting_for_vrp (gimple stmt)
   else if (is_gimple_assign (stmt) || is_gimple_call (stmt))
     {
       tree lhs = gimple_get_lhs (stmt);
 
       /* In general, assignments with virtual operands are not useful
         for deriving ranges, with the obvious exception of calls to
         builtin functions.  */
       if (lhs && TREE_CODE (lhs) == SSA_NAME
          && (INTEGRAL_TYPE_P (TREE_TYPE (lhs))
              || POINTER_TYPE_P (TREE_TYPE (lhs)))
-         && ((is_gimple_call (stmt)
-              && gimple_call_fndecl (stmt) != NULL_TREE
-              && (DECL_BUILT_IN (gimple_call_fndecl (stmt))
-                  || DECL_IS_OPERATOR_NEW (gimple_call_fndecl (stmt))))
+         && (is_gimple_call (stmt)
              || !gimple_vuse (stmt)))
        return true;
     }
   else if (gimple_code (stmt) == GIMPLE_COND
           || gimple_code (stmt) == GIMPLE_SWITCH)
     return true;
 
   return false;
 }
 

Reply via email to