https://gcc.gnu.org/g:19fe55c4801de50deee03b333e94d007aae222e3

commit r15-5750-g19fe55c4801de50deee03b333e94d007aae222e3
Author: Jakub Jelinek <ja...@redhat.com>
Date:   Thu Nov 28 11:48:33 2024 +0100

    Add support for nonnull_if_nonzero attribute [PR117023]
    
    As mentioned in an earlier thread, C2Y voted in a change which made
    various library APIs callable with NULL arguments in certain cases,
    e.g.
    memcpy (NULL, NULL, 0);
    is now valid, although
    memcpy (NULL, NULL, 1);
    remains invalid.  This affects various APIs, including several of
    GCC builtins; plus on the C library side those APIs are often declared
    with nonnull attribute(s) as well.
    
    Florian suggested using the access attribute for this, but our docs
    explicitly say that access attribute doesn't imply nonnull and it doesn't
    cover e.g. the qsort case where the comparison function pointer may be
    also NULL if nmemb is 0, but must be non-zero otherwise.
    As this case affects 21 APIs in C standard and I think is going to affect
    various wrappers around those in various packages as well, I think it
    is a common thing that should have its own attribute, because we should
    still warn when people use
    qsort (NULL, 1, 1, NULL);
    etc., and similarly want to have -fsanitize=null instrumentation for those.
    
    So, the following patch introduces nonnull_if_nonzero attribute (or would
    you prefer cond_nonnull or some other name?), which has always 2 arguments,
    argument index of a pointer argument (like one argument nonnull) and
    argument index of an associated integral argument.  If that argument is
    non-zero, it is UB to pass NULL to the pointer argument, if that argument
    is zero, it is valid.  And changes various spots which already handled the
    nonnull attribute to handle this one as well, with sometimes using the
    ranger (or for -fsanitize=nonnull explicitly checking the associated
    argument value, so instead of if (!ptr) __ubsan_... (...); it will
    now do if (!ptr && sz) __ubsan_... (...);).
    I've so far omitted changing gimple_infer_range (am not 100% sure how I can
    use the ranger inside of the ranger) and changing the analyzer to handle it.
    And I haven't changed builtins.def etc. to make use of that attribute
    instead of nonnull where appropriate.
    
    I'd then follow with the builtins.def changes (and eventually glibc
    etc. would need to be adjusted too).
    
    2024-11-28  Jakub Jelinek  <ja...@redhat.com>
    
            PR c/117023
    gcc/
            * gimple.h (infer_nonnull_range_by_attribute): Add a tree *
            argument defaulted to NULL.
            * gimple.cc (infer_nonnull_range_by_attribute): Add op2 argument.
            Handle also nonnull_if_nonzero attributes.
            * tree.cc (get_nonnull_args): Fix comment typo.
            * builtins.cc (validate_arglist): Handle nonnull_if_nonzero 
attribute.
            * tree-ssa-ccp.cc (pass_post_ipa_warn::execute): Handle
            nonnull_if_nonzero attributes.
            * ubsan.cc (instrument_nonnull_arg): Adjust
            infer_nonnull_range_by_attribute caller.  If it returned true and
            filed in non-NULL arg2, check that arg2 is non-zero as another
            condition next to checking that arg is zero.
            * doc/extend.texi (nonnull_if_nonzero): Document new attribute.
    gcc/c-family/
            * c-attribs.cc (handle_nonnull_if_nonzero_attribute): New
            function.
            (c_common_gnu_attributes): Add nonnull_if_nonzero attribute.
            (handle_nonnull_attribute): Fix comment typo.
            * c-common.cc (struct nonnull_arg_ctx): Add other member.
            (check_function_nonnull): Also check nonnull_if_nonzero attributes.
            (check_nonnull_arg): Use different warning wording if pctx->other
            is non-zero.
            (check_function_arguments): Initialize ctx.other.
    gcc/testsuite/
            * gcc.dg/nonnull-8.c: New test.
            * gcc.dg/nonnull-9.c: New test.
            * gcc.dg/nonnull-10.c: New test.
            * c-c++-common/ubsan/nonnull-6.c: New test.
            * c-c++-common/ubsan/nonnull-7.c: New test.

Diff:
---
 gcc/builtins.cc                              |  18 +++
 gcc/c-family/c-attribs.cc                    |  29 ++++-
 gcc/c-family/c-common.cc                     |  54 +++++++--
 gcc/doc/extend.texi                          |  35 +++++-
 gcc/gimple.cc                                |  47 +++++++-
 gcc/gimple.h                                 |   2 +-
 gcc/testsuite/c-c++-common/ubsan/nonnull-6.c |  28 +++++
 gcc/testsuite/c-c++-common/ubsan/nonnull-7.c |  39 +++++++
 gcc/testsuite/gcc.dg/nonnull-10.c            | 162 +++++++++++++++++++++++++++
 gcc/testsuite/gcc.dg/nonnull-8.c             |  57 ++++++++++
 gcc/testsuite/gcc.dg/nonnull-9.c             |  40 +++++++
 gcc/tree-ssa-ccp.cc                          |  68 ++++++++++-
 gcc/tree.cc                                  |   2 +-
 gcc/ubsan.cc                                 |  22 +++-
 14 files changed, 577 insertions(+), 26 deletions(-)

diff --git a/gcc/builtins.cc b/gcc/builtins.cc
index d925074c5475..fd7acdfc915b 100644
--- a/gcc/builtins.cc
+++ b/gcc/builtins.cc
@@ -1149,6 +1149,24 @@ validate_arglist (const_tree callexpr, ...)
 
   BITMAP_FREE (argmap);
 
+  if (res)
+    for (tree attrs = TYPE_ATTRIBUTES (TREE_TYPE (TREE_TYPE (fn)));
+        (attrs = lookup_attribute ("nonnull_if_nonzero", attrs));
+        attrs = TREE_CHAIN (attrs))
+      {
+       tree args = TREE_VALUE (attrs);
+       unsigned int idx = TREE_INT_CST_LOW (TREE_VALUE (args)) - 1;
+       unsigned int idx2
+         = TREE_INT_CST_LOW (TREE_VALUE (TREE_CHAIN (args))) - 1;
+       if (idx < (unsigned) call_expr_nargs (callexpr)
+           && idx2 < (unsigned) call_expr_nargs (callexpr)
+           && POINTER_TYPE_P (TREE_TYPE (CALL_EXPR_ARG (callexpr, idx)))
+           && integer_zerop (CALL_EXPR_ARG (callexpr, idx))
+           && INTEGRAL_TYPE_P (TREE_TYPE (CALL_EXPR_ARG (callexpr, idx2)))
+           && integer_nonzerop (CALL_EXPR_ARG (callexpr, idx2)))
+         return false;
+      }
+
   return res;
 }
 
diff --git a/gcc/c-family/c-attribs.cc b/gcc/c-family/c-attribs.cc
index 5a71749d2f9c..5b64805f97de 100644
--- a/gcc/c-family/c-attribs.cc
+++ b/gcc/c-family/c-attribs.cc
@@ -138,6 +138,8 @@ static tree handle_vector_size_attribute (tree *, tree, 
tree, int,
 static tree handle_vector_mask_attribute (tree *, tree, tree, int,
                                          bool *) ATTRIBUTE_NONNULL(3);
 static tree handle_nonnull_attribute (tree *, tree, tree, int, bool *);
+static tree handle_nonnull_if_nonzero_attribute (tree *, tree, tree, int,
+                                                bool *);
 static tree handle_nonstring_attribute (tree *, tree, tree, int, bool *);
 static tree handle_nothrow_attribute (tree *, tree, tree, int, bool *);
 static tree handle_expected_throw_attribute (tree *, tree, tree, int, bool *);
@@ -487,6 +489,8 @@ const struct attribute_spec c_common_gnu_attributes[] =
                              handle_tls_model_attribute, NULL },
   { "nonnull",                0, -1, false, true, true, false,
                              handle_nonnull_attribute, NULL },
+  { "nonnull_if_nonzero",     2, 2, false, true, true, false,
+                             handle_nonnull_if_nonzero_attribute, NULL },
   { "nonstring",              0, 0, true, false, false, false,
                              handle_nonstring_attribute, NULL },
   { "nothrow",                0, 0, true,  false, false, false,
@@ -5002,7 +5006,7 @@ handle_nonnull_attribute (tree *node, tree name,
       /* NEXT is null when the attribute includes just one argument.
         That's used to tell positional_argument to avoid mentioning
         the argument number in diagnostics (since there's just one
-        mentioning it is unnecessary and coule be confusing).  */
+        mentioning it is unnecessary and could be confusing).  */
       tree next = TREE_CHAIN (args);
       if (tree val = positional_argument (type, name, pos, POINTER_TYPE,
                                          next || i > 1 ? i : 0))
@@ -5018,6 +5022,29 @@ handle_nonnull_attribute (tree *node, tree name,
   return NULL_TREE;
 }
 
+/* Handle the "nonnull_if_nonzero" attribute.  */
+
+static tree
+handle_nonnull_if_nonzero_attribute (tree *node, tree name,
+                                    tree args, int ARG_UNUSED (flags),
+                                    bool *no_add_attrs)
+{
+  tree type = *node;
+  tree pos = TREE_VALUE (args);
+  tree pos2 = TREE_VALUE (TREE_CHAIN (args));
+  tree val = positional_argument (type, name, pos, POINTER_TYPE, 1);
+  tree val2 = positional_argument (type, name, pos2, INTEGER_TYPE, 2);
+  if (val && val2)
+    {
+      TREE_VALUE (args) = val;
+      TREE_VALUE (TREE_CHAIN (args)) = val2;
+    }
+  else
+    *no_add_attrs = true;
+
+  return NULL_TREE;
+}
+
 /* Handle the "fd_arg", "fd_arg_read" and "fd_arg_write" attributes */
 
 static tree
diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
index 721407157bc3..a8f25d6cb944 100644
--- a/gcc/c-family/c-common.cc
+++ b/gcc/c-family/c-common.cc
@@ -5721,6 +5721,8 @@ struct nonnull_arg_ctx
   /* The function whose arguments are being checked and its type (used
      for calls through function pointers).  */
   const_tree fndecl, fntype;
+  /* For nonnull_if_nonzero, index of the other argument.  */
+  unsigned HOST_WIDE_INT other;
   /* True if a warning has been issued.  */
   bool warned_p;
 };
@@ -5759,23 +5761,19 @@ check_function_nonnull (nonnull_arg_ctx &ctx, int 
nargs, tree *argarray)
     }
 
   tree attrs = lookup_attribute ("nonnull", TYPE_ATTRIBUTES (ctx.fntype));
-  if (attrs == NULL_TREE)
-    return ctx.warned_p;
 
   tree a = attrs;
   /* See if any of the nonnull attributes has no arguments.  If so,
      then every pointer argument is checked (in which case the check
      for pointer type is done in check_nonnull_arg).  */
-  if (TREE_VALUE (a) != NULL_TREE)
-    do
-      a = lookup_attribute ("nonnull", TREE_CHAIN (a));
-    while (a != NULL_TREE && TREE_VALUE (a) != NULL_TREE);
+  while (a != NULL_TREE && TREE_VALUE (a) != NULL_TREE)
+    a = lookup_attribute ("nonnull", TREE_CHAIN (a));
 
   if (a != NULL_TREE)
     for (int i = firstarg; i < nargs; i++)
       check_function_arguments_recurse (check_nonnull_arg, &ctx, argarray[i],
                                        i + 1, OPT_Wnonnull);
-  else
+  else if (attrs)
     {
       /* Walk the argument list.  If we encounter an argument number we
         should check for non-null, do it.  */
@@ -5794,6 +5792,28 @@ check_function_nonnull (nonnull_arg_ctx &ctx, int nargs, 
tree *argarray)
                                              OPT_Wnonnull);
        }
     }
+  if (a == NULL_TREE)
+    for (attrs = TYPE_ATTRIBUTES (ctx.fntype);
+        (attrs = lookup_attribute ("nonnull_if_nonzero", attrs));
+        attrs = TREE_CHAIN (attrs))
+      {
+       tree args = TREE_VALUE (attrs);
+       unsigned int idx = TREE_INT_CST_LOW (TREE_VALUE (args)) - 1;
+       unsigned int idx2
+         = TREE_INT_CST_LOW (TREE_VALUE (TREE_CHAIN (args))) - 1;
+       if (idx < (unsigned) nargs - firstarg
+           && idx2 < (unsigned) nargs - firstarg
+           && INTEGRAL_TYPE_P (TREE_TYPE (argarray[firstarg + idx2]))
+           && integer_nonzerop (argarray[firstarg + idx2]))
+         {
+           ctx.other = firstarg + idx2 + 1;
+           check_function_arguments_recurse (check_nonnull_arg, &ctx,
+                                             argarray[firstarg + idx],
+                                             firstarg + idx + 1,
+                                             OPT_Wnonnull);
+           ctx.other = 0;
+         }
+      }
   return ctx.warned_p;
 }
 
@@ -5975,13 +5995,23 @@ check_nonnull_arg (void *ctx, tree param, unsigned 
HOST_WIDE_INT param_num)
     }
   else
     {
-      warned = warning_at (loc, OPT_Wnonnull,
-                          "argument %u null where non-null expected",
-                          (unsigned) param_num);
+      if (pctx->other)
+       warned = warning_at (loc, OPT_Wnonnull,
+                            "argument %u null where non-null expected "
+                            "because argument %u is nonzero",
+                            (unsigned) param_num,
+                            TREE_CODE (pctx->fntype) == METHOD_TYPE
+                            ? (unsigned) pctx->other - 1
+                            : (unsigned) pctx->other);
+      else
+       warned = warning_at (loc, OPT_Wnonnull,
+                            "argument %u null where non-null expected",
+                            (unsigned) param_num);
       if (warned && pctx->fndecl)
        inform (DECL_SOURCE_LOCATION (pctx->fndecl),
                "in a call to function %qD declared %qs",
-               pctx->fndecl, "nonnull");
+               pctx->fndecl,
+               pctx->other ? "nonnull_if_nonzero" : "nonnull");
     }
 
   if (warned)
@@ -6227,7 +6257,7 @@ check_function_arguments (location_t loc, const_tree 
fndecl, const_tree fntype,
      to do this if format checking is enabled.  */
   if (warn_nonnull)
     {
-      nonnull_arg_ctx ctx = { loc, fndecl, fntype, false };
+      nonnull_arg_ctx ctx = { loc, fndecl, fntype, 0, false };
       warned_p = check_function_nonnull (ctx, nargs, argarray);
     }
 
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 2fc513efdb58..8497aa8603f2 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -2754,9 +2754,10 @@ object size, for example in functions that call 
@code{__builtin_object_size}.
 Note that the @code{access} attribute merely specifies how an object
 referenced by the pointer argument can be accessed; it does not imply that
 an access @strong{will} happen.  Also, the @code{access} attribute does not
-imply the attribute @code{nonnull}; it may be appropriate to add both 
attributes
-at the declaration of a function that unconditionally manipulates a buffer via
-a pointer argument.  See the @code{nonnull} attribute for more information and
+imply the attribute @code{nonnull} nor the attribute @code{nonnull_if_nonzero};
+it may be appropriate to add both attributes at the declaration of a function
+that unconditionally manipulates a buffer via a pointer argument.  See the
+@code{nonnull} or @code{nonnull_if_nonzero} attributes for more information and
 caveats.
 
 @cindex @code{alias} function attribute
@@ -3788,6 +3789,34 @@ my_memcpy (void *dest, const void *src, size_t len)
         __attribute__((nonnull));
 @end smallexample
 
+@cindex @code{nonnull_if_nonzero} function attribute
+@item nonnull_if_nonzero
+@itemx nonnull_if_nonzero (@var{arg-index}, @var{arg2-index})
+The @code{nonnull_if_nonzero} attribute is a conditional version of the
+@code{nonnull} attribute.  It has two arguments, the first argument
+shall be argument index of a pointer argument which must be in some
+cases non-null and the second argument shall be argument index of an
+integral argument (other than boolean).  If the integral argument is
+zero, the pointer argument can be null, if it is non-zero, the pointer
+argument must not be null.
+
+@smallexample
+extern void *
+my_memcpy (void *dest, const void *src, size_t len)
+        __attribute__((nonnull (1, 2)));
+extern void *
+my_memcpy2 (void *dest, const void *src, size_t len)
+        __attribute__((nonnull_if_nonzero (1, 3),
+                       nonnull_if_nonzero (2, 3)));
+@end smallexample
+
+With these declarations, it is invalid to call
+@code{my_memcpy (NULL, NULL, 0);} or to
+call @code{my_memcpy2 (NULL, NULL, 4);} but it is valid
+to call @code{my_memcpy2 (NULL, NULL, 0);}.  This attribute should be
+used on declarations which have e.g.@: an exception for zero sizes,
+in which case null may be passed.
+
 @cindex @code{noplt} function attribute
 @item noplt
 The @code{noplt} attribute is the counterpart to option @option{-fno-plt}.
diff --git a/gcc/gimple.cc b/gcc/gimple.cc
index c6d0991ded90..477315cb1b86 100644
--- a/gcc/gimple.cc
+++ b/gcc/gimple.cc
@@ -3142,10 +3142,17 @@ infer_nonnull_range_by_dereference (gimple *stmt, tree 
op)
 }
 
 /* Return true if OP can be inferred to be a non-NULL after STMT
-   executes by using attributes.  */
+   executes by using attributes.  If OP2 is non-NULL and nonnull_if_nonzero
+   is the only attribute implying OP being non-NULL and the corresponding
+   argument isn't non-zero INTEGER_CST, set *OP2 to the corresponding
+   argument and return true (in that case returning true doesn't mean
+   OP can be unconditionally inferred to be non-NULL, but conditionally).  */
 bool
-infer_nonnull_range_by_attribute (gimple *stmt, tree op)
+infer_nonnull_range_by_attribute (gimple *stmt, tree op, tree *op2)
 {
+  if (op2)
+    *op2 = NULL_TREE;
+
   /* 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
@@ -3162,9 +3169,10 @@ infer_nonnull_range_by_attribute (gimple *stmt, tree op)
          attrs = lookup_attribute ("nonnull", attrs);
 
          /* If "nonnull" wasn't specified, we know nothing about
-            the argument.  */
+            the argument, unless "nonnull_if_nonzero" attribute is
+            present.  */
          if (attrs == NULL_TREE)
-           return false;
+           break;
 
          /* If "nonnull" applies to all the arguments, then ARG
             is non-null if it's in the argument list.  */
@@ -3191,6 +3199,37 @@ infer_nonnull_range_by_attribute (gimple *stmt, tree op)
                }
            }
        }
+
+      for (attrs = TYPE_ATTRIBUTES (fntype);
+          (attrs = lookup_attribute ("nonnull_if_nonzero", attrs));
+          attrs = TREE_CHAIN (attrs))
+       {
+         tree args = TREE_VALUE (attrs);
+         unsigned int idx = TREE_INT_CST_LOW (TREE_VALUE (args)) - 1;
+         unsigned int idx2
+           = TREE_INT_CST_LOW (TREE_VALUE (TREE_CHAIN (args))) - 1;
+         if (idx < gimple_call_num_args (stmt)
+             && idx2 < gimple_call_num_args (stmt)
+             && operand_equal_p (op, gimple_call_arg (stmt, idx), 0))
+           {
+             tree arg2 = gimple_call_arg (stmt, idx2);
+             if (!INTEGRAL_TYPE_P (TREE_TYPE (arg2)))
+               return false;
+             if (integer_nonzerop (arg2))
+               return true;
+             if (integer_zerop (arg2))
+               return false;
+             if (op2)
+               {
+                 /* This case is meant for ubsan instrumentation.
+                    The caller can check at runtime if *OP2 is
+                    non-zero and OP is null.  */
+                 *op2 = arg2;
+                 return true;
+               }
+             return tree_expr_nonzero_p (arg2);
+           }
+       }
     }
 
   /* If this function is marked as returning non-null, then we can
diff --git a/gcc/gimple.h b/gcc/gimple.h
index b6967e63de23..039ed66eab5d 100644
--- a/gcc/gimple.h
+++ b/gcc/gimple.h
@@ -1662,7 +1662,7 @@ extern bool nonfreeing_call_p (gimple *);
 extern bool nonbarrier_call_p (gimple *);
 extern bool infer_nonnull_range (gimple *, tree);
 extern bool infer_nonnull_range_by_dereference (gimple *, tree);
-extern bool infer_nonnull_range_by_attribute (gimple *, tree);
+extern bool infer_nonnull_range_by_attribute (gimple *, tree, tree * = NULL);
 extern void sort_case_labels (vec<tree> &);
 extern void preprocess_case_label_vec_for_gimple (vec<tree> &, tree, tree *);
 extern void gimple_seq_set_location (gimple_seq, location_t);
diff --git a/gcc/testsuite/c-c++-common/ubsan/nonnull-6.c 
b/gcc/testsuite/c-c++-common/ubsan/nonnull-6.c
new file mode 100644
index 000000000000..8e072feed89a
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/ubsan/nonnull-6.c
@@ -0,0 +1,28 @@
+/* { dg-do run } */
+/* { dg-options "-fsanitize=undefined -fno-sanitize-recover=undefined" } */
+
+__attribute__((noipa, nonnull_if_nonzero (1, 4)))
+__attribute__((nonnull (3), nonnull_if_nonzero (5, 2))) void
+foo (void *a, unsigned long b, void *c, int d, void *e)
+{
+  (void) a;
+  (void) b;
+  (void) c;
+  (void) d;
+  (void) e;
+}
+
+__attribute__((noipa))
+void
+bar (void *a, unsigned long b, void *c, int d, void *e)
+{
+  foo (a, b, c, d, e);
+}
+
+int
+main ()
+{
+  char x;
+  bar (&x, 42, &x, 1, &x);
+  bar (0, 0, &x, 0, 0);
+}
diff --git a/gcc/testsuite/c-c++-common/ubsan/nonnull-7.c 
b/gcc/testsuite/c-c++-common/ubsan/nonnull-7.c
new file mode 100644
index 000000000000..a8be2a7a376e
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/ubsan/nonnull-7.c
@@ -0,0 +1,39 @@
+/* { dg-do run } */
+/* { dg-options "-fsanitize=nonnull-attribute" } */
+
+__attribute__((noipa, nonnull_if_nonzero (1, 4)))
+__attribute__((nonnull (3), nonnull_if_nonzero (5, 2))) void
+foo (void *a, unsigned long b, void *c, int d, void *e)
+{
+  (void) a;
+  (void) b;
+  (void) c;
+  (void) d;
+  (void) e;
+}
+
+__attribute__((noipa))
+void
+bar (void *a, unsigned long b, void *c, int d, void *e)
+{
+  foo (a, b, c, d, e);
+}
+
+int
+main ()
+{
+  char x;
+  bar (&x, 42, 0, 1, &x);
+  bar (0, 25, &x, 7, &x);
+  bar (&x, -82, &x, 68, 0);
+  foo (&x, 42, 0, 1, &x);
+  foo (0, 25, &x, 7, &x);
+  foo (&x, -82, &x, 68, 0);
+}
+
+/* { dg-output "\.c:19:\[0-9]*:\[^\n\r]*null pointer passed as argument 3, 
which is declared to never be null\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\.c:19:\[0-9]*:\[^\n\r]*null pointer passed as 
argument 1, which is declared to never be null\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\.c:19:\[0-9]*:\[^\n\r]*null pointer passed as 
argument 5, which is declared to never be null\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\.c:29:\[0-9]*:\[^\n\r]*null pointer passed as 
argument 3, which is declared to never be null\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\.c:30:\[0-9]*:\[^\n\r]*null pointer passed as 
argument 1, which is declared to never be null\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\.c:31:\[0-9]*:\[^\n\r]*null pointer passed as 
argument 5, which is declared to never be null" } */
diff --git a/gcc/testsuite/gcc.dg/nonnull-10.c 
b/gcc/testsuite/gcc.dg/nonnull-10.c
new file mode 100644
index 000000000000..447ad301275a
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/nonnull-10.c
@@ -0,0 +1,162 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -Wnonnull" } */
+
+#define N(x, y) __attribute__ ((nonnull_if_nonzero (x, y)))
+
+void N (1, 2) f1_1 (void *, int);
+
+void N (1, 3) f2_1 (void *, void *, int);
+void N (1, 3) N (2, 3) f2_1_2 (void *, void *, int);
+
+void N (1, 4) N (3, 5) f3_1_3 (void *, void *, void *, int, int);
+
+void N (1, 5) N (2, 5) N (4, 5) g4_1_2_4 (void *, void *, void *, void *, 
long);
+void N (1, 5) N (3, 5) N (4, 5) g4_1_3_4 (void *, void *, void *, void *, 
long);
+void N (2, 5) N (3, 5) N (4, 5) g4_2_3_4 (void *, void *, void *, void *, 
long);
+
+void N (1, 17) N (3, 17) N (5, 17) N (7, 17) N (11, 17) N (13, 17)
+g16_1_3_5_7_11_13 (void *, void *, void *, void *,
+                  void *, void *, void *, void *,
+                  void *, void *, void *, void *,
+                  void *, void *, void *, void *, int);
+
+static void *null (void) { return 0; }
+
+void
+test (int t, long u)
+{
+  void *p0 = null ();
+  void *px = &px;
+
+  f1_1 (p0, 0);
+  f1_1 (p0, t);
+  f1_1 (p0, 42); /* { dg-warning "argument 1 null where non-null expected 
because argument 2 is nonzero" } */
+  if (t)
+    f1_1 (p0, t); /* { dg-warning "argument 1 null where non-null expected 
because argument 2 is nonzero" } */
+  f1_1 (px, 17);
+
+  f2_1 (p0, px, 0);
+  f2_1 (p0, px, t);
+  f2_1 (p0, px, 5);  /* { dg-warning "argument 1 null where non-null expected 
because argument 3 is nonzero" } */
+  if (t > 4)
+    f2_1 (p0, px, t);  /* { dg-warning "argument 1 null where non-null 
expected because argument 3 is nonzero" } */
+  f2_1 (px, p0, 17);
+  f2_1 (p0, p0, 0);
+  if (t < 0)
+    f2_1 (p0, p0, t);  /* { dg-warning "argument 1 null where non-null 
expected because argument 3 is nonzero" } */
+
+  f2_1_2 (p0, p0, 0);
+  f2_1_2 (p0, p0, t);
+  f2_1_2 (p0, px, 1); /* { dg-warning "argument 1 null where non-null expected 
because argument 3 is nonzero" } */
+  if (t > 8)
+    f2_1_2 (p0, px, t); /* { dg-warning "argument 1 null where non-null 
expected because argument 3 is nonzero" } */
+  f2_1_2 (px, p0, -3); /* { dg-warning "argument 2 null where non-null 
expected because argument 3 is nonzero" } */
+  if (t < -2)
+    f2_1_2 (px, p0, t); /* { dg-warning "argument 2 null where non-null 
expected because argument 3 is nonzero" } */
+  f2_1_2 (p0, p0, 8); /* { dg-warning "argument 1 null where non-null expected 
because argument 3 is nonzero" } */
+  /* { dg-warning "argument 2 null where non-null expected because argument 3 
is nonzero" "argument 2" { target *-*-* } .-1 } */
+  if (t > 7)
+    f2_1_2 (p0, p0, t); /* { dg-warning "argument 1 null where non-null 
expected because argument 3 is nonzero" } */
+  /* { dg-warning "argument 2 null where non-null expected because argument 3 
is nonzero" "argument 2" { target *-*-* } .-1 } */
+
+  f3_1_3 (p0, p0, p0, 0, 0);
+  f3_1_3 (p0, p0, px, 0, 6);
+  f3_1_3 (px, p0, p0, 2, 0);
+  f3_1_3 (p0, p0, p0, t, t);
+  f3_1_3 (p0, p0, px, t, 6);
+  f3_1_3 (px, p0, p0, 2, t);
+  f3_1_3 (p0, px, px, 8, 2); /* { dg-warning "argument 1 null where non-null 
expected because argument 4 is nonzero" } */
+  if (t > 9)
+    f3_1_3 (p0, px, px, t, 3); /* { dg-warning "argument 1 null where non-null 
expected because argument 4 is nonzero" } */
+  f3_1_3 (px, p0, px, 9, 10);
+  if (t > 11)
+    f3_1_3 (px, p0, px, t, t);
+  f3_1_3 (px, px, p0, 10, 11); /* { dg-warning "argument 3 null where non-null 
expected because argument 5 is nonzero" } */
+  if (t < -5)
+    f3_1_3 (px, px, p0, 0, t); /* { dg-warning "argument 3 null where non-null 
expected because argument 5 is nonzero" } */
+  f3_1_3 (p0, p0, px, 11, 12); /* { dg-warning "argument 1 null where non-null 
expected because argument 4 is nonzero" } */
+  if (t > 26)
+    f3_1_3 (p0, p0, px, t, 0); /* { dg-warning "argument 1 null where non-null 
expected because argument 4 is nonzero" } */
+  f3_1_3 (px, p0, p0, 12, 13); /* { dg-warning "argument 3 null where non-null 
expected because argument 5 is nonzero" } */
+  if (t > 31)
+    f3_1_3 (px, p0, p0, 12, t); /* { dg-warning "argument 3 null where 
non-null expected because argument 5 is nonzero" } */
+  f3_1_3 (p0, p0, p0, 13, 14); /* { dg-warning "argument 1 null where non-null 
expected because argument 4 is nonzero" } */
+  /* { dg-warning "argument 3 null where non-null expected because argument 5 
is nonzero" "argument 3" { target *-*-* } .-1 } */
+  if (t > 28)
+    f3_1_3 (p0, p0, p0, t, t + 1); /* { dg-warning "argument 1 null where 
non-null expected because argument 4 is nonzero" } */
+  /* { dg-warning "argument 3 null where non-null expected because argument 5 
is nonzero" "argument 3" { target *-*-* } .-1 } */
+
+  g4_1_2_4 (p0, px, px, px, u);
+  g4_1_2_4 (px, p0, px, px, u);
+  g4_1_2_4 (px, px, p0, px, u);
+  g4_1_2_4 (px, px, px, p0, u);
+  g4_1_2_4 (p0, px, px, px, 0);
+  g4_1_2_4 (px, p0, px, px, 0);
+  g4_1_2_4 (px, px, p0, px, 0);
+  g4_1_2_4 (px, px, px, p0, 0);
+  g4_1_2_4 (p0, px, px, px, 15); /* { dg-warning "argument 1 null where 
non-null expected because argument 5 is nonzero" } */
+  if (u)
+    g4_1_2_4 (p0, px, px, px, u); /* { dg-warning "argument 1 null where 
non-null expected because argument 5 is nonzero" } */
+  g4_1_2_4 (px, p0, px, px, 16); /* { dg-warning "argument 2 null where 
non-null expected because argument 5 is nonzero" } */
+  if (u > 2)
+    g4_1_2_4 (px, p0, px, px, u); /* { dg-warning "argument 2 null where 
non-null expected because argument 5 is nonzero" } */
+  g4_1_2_4 (px, px, p0, px, 17);
+  if (u > 3)
+    g4_1_2_4 (px, px, p0, px, u);
+  g4_1_2_4 (px, px, px, p0, 18); /* { dg-warning "argument 4 null where 
non-null expected because argument 5 is nonzero" } */
+  if (u < -2 || u > 10)
+    g4_1_2_4 (px, px, px, p0, u); /* { dg-warning "argument 4 null where 
non-null expected because argument 5 is nonzero" } */
+
+  g4_1_3_4 (p0, px, px, px, u);
+  g4_1_3_4 (px, p0, px, px, u);
+  g4_1_3_4 (px, px, p0, px, u);
+  g4_1_3_4 (px, px, px, p0, u);
+  g4_1_3_4 (p0, px, px, px, 0);
+  g4_1_3_4 (px, p0, px, px, 0);
+  g4_1_3_4 (px, px, p0, px, 0);
+  g4_1_3_4 (px, px, px, p0, 0);
+  g4_1_3_4 (p0, px, px, px, 20); /* { dg-warning "argument 1 null where 
non-null expected because argument 5 is nonzero" } */
+  if (u > 4)
+    g4_1_3_4 (p0, px, px, px, u); /* { dg-warning "argument 1 null where 
non-null expected because argument 5 is nonzero" } */
+  g4_1_3_4 (px, p0, px, px, 21);
+  if (u > 6 || u < -24)
+    g4_1_3_4 (px, p0, px, px, u);
+  g4_1_3_4 (px, px, p0, px, 22); /* { dg-warning "argument 3 null where 
non-null expected because argument 5 is nonzero" } */
+  if (u > 9)
+    g4_1_3_4 (px, px, p0, px, u - 3); /* { dg-warning "argument 3 null where 
non-null expected because argument 5 is nonzero" } */
+  g4_1_3_4 (px, px, px, p0, 23); /* { dg-warning "argument 4 null where 
non-null expected because argument 5 is nonzero" } */
+  if (u > 10)
+    g4_1_3_4 (px, px, px, p0, u); /* { dg-warning "argument 4 null where 
non-null expected because argument 5 is nonzero" } */
+
+  g4_2_3_4 (p0, px, px, px, u);
+  g4_2_3_4 (px, p0, px, px, u);
+  g4_2_3_4 (px, px, p0, px, u);
+  g4_2_3_4 (px, px, px, p0, u);
+  g4_2_3_4 (p0, px, px, px, 0);
+  g4_2_3_4 (px, p0, px, px, 0);
+  g4_2_3_4 (px, px, p0, px, 0);
+  g4_2_3_4 (px, px, px, p0, 0);
+  g4_2_3_4 (p0, px, px, px, 1);
+  if (u > 12)
+    g4_2_3_4 (p0, px, px, px, u);
+  g4_2_3_4 (px, p0, px, px, 2); /* { dg-warning "argument 2 null where 
non-null expected because argument 5 is nonzero" } */
+  if (u > 17)
+    g4_2_3_4 (px, p0, px, px, u - 3); /* { dg-warning "argument 2 null where 
non-null expected because argument 5 is nonzero" } */
+  g4_2_3_4 (px, px, p0, px, 3); /* { dg-warning "argument 3 null where 
non-null expected because argument 5 is nonzero" } */
+  if (u > 24)
+    g4_2_3_4 (px, px, p0, px, u); /* { dg-warning "argument 3 null where 
non-null expected because argument 5 is nonzero" } */
+  g4_2_3_4 (px, px, px, p0, 4); /* { dg-warning "argument 4 null where 
non-null expected because argument 5 is nonzero" } */
+  if (u > 42)
+    g4_2_3_4 (px, px, px, p0, u); /* { dg-warning "argument 4 null where 
non-null expected because argument 5 is nonzero" } */
+
+  g16_1_3_5_7_11_13 (px, px, px, px, px, px, px, px,
+                    px, px, px, px, px, px, px, px, 17);
+  g16_1_3_5_7_11_13 (p0, p0, p0, p0, p0, p0, p0, p0,
+                    p0, p0, p0, p0, p0, p0, p0, p0, t);
+  g16_1_3_5_7_11_13 (p0, p0, p0, p0, p0, p0, p0, p0,
+                    p0, p0, p0, p0, p0, p0, p0, p0, 0);
+
+  g16_1_3_5_7_11_13 (px, p0, px, p0, px, p0, px, p0, p0, p0, px, p0, p0, p0, 
p0, p0, 2); /* { dg-warning "argument 13 null where non-null expected because 
argument 17 is nonzero" } */
+  if (t > 122)
+    g16_1_3_5_7_11_13 (px, p0, px, p0, px, p0, px, p0, p0, p0, px, p0, p0, p0, 
p0, p0, t); /* { dg-warning "argument 13 null where non-null expected because 
argument 17 is nonzero" } */
+}
diff --git a/gcc/testsuite/gcc.dg/nonnull-8.c b/gcc/testsuite/gcc.dg/nonnull-8.c
new file mode 100644
index 000000000000..64e36f9d96ee
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/nonnull-8.c
@@ -0,0 +1,57 @@
+/* Test for the "nonnull_if_nonzero" function attribute.  */
+/* { dg-do compile } */
+/* { dg-options "-Wnonnull" } */
+
+#include <stddef.h>
+
+extern void func1 (char *, char *, int)
+  __attribute__((nonnull_if_nonzero (1, 3), nonnull_if_nonzero (2, 3)));
+
+extern void func2 (char *, char *, unsigned long)
+  __attribute__((nonnull_if_nonzero (1, 3)));
+
+enum E { E0 = 0, E1 = __INT_MAX__ };
+extern void func3 (char *, int, char *, enum E)
+  __attribute__((nonnull_if_nonzero (1, 4), nonnull_if_nonzero (3, 2)));
+
+extern void func4 (long, char *, char *, long)
+  __attribute__((nonnull_if_nonzero (2, 1)))
+  __attribute__((nonnull_if_nonzero (3, 4)));
+
+void
+foo (int i1, int i2, int i3, char *cp1, char *cp2, char *cp3)
+{
+  func1 (cp1, cp2, i1);
+  func1 (cp1, cp2, 0);
+  func1 (cp1, cp2, 42);
+  func1 (NULL, NULL, 0);
+  func1 (NULL, NULL, i1);
+
+  func1 (NULL, cp2, 42); /* { dg-warning "argument 1 null where non-null 
expected because argument 3 is nonzero" } */
+  func1 (cp1, NULL, 1); /* { dg-warning "argument 2 null where non-null 
expected because argument 3 is nonzero" } */
+
+  func2 (cp1, NULL, 17);
+  func2 (NULL, cp2, 0);
+  func2 (NULL, cp1, 2); /* { dg-warning "argument 1 null where non-null 
expected because argument 3 is nonzero" } */
+
+  func3 (NULL, i2, cp3, i3);
+  func3 (cp1, i2, NULL, i3);
+  func3 (NULL, i2, cp3, E0);
+  func3 (cp1, 0, NULL, E1);
+  func3 (NULL, i2, cp3, E1); /* { dg-warning "argument 1 null where non-null 
expected because argument 4 is nonzero" } */
+  func3 (cp3, 5, NULL, i3); /* { dg-warning "argument 3 null where non-null 
expected because argument 2 is nonzero" } */
+
+  func1 (i2 ? cp1 : NULL, cp2, i3);
+  func1 (i2 ? NULL : cp1, cp2, i3);
+  func1 (i2 ? (i3 ? cp1 : NULL) : cp2, cp3, i1);
+  func1 (i1 ? cp1 : NULL, cp2, 0);
+  func1 (i1 ? NULL : cp1, cp2, 0);
+  func1 (i1 ? (i2 ? cp1 : NULL) : cp2, cp3, 0);
+  func1 (i1 ? cp1 : NULL, cp2, 1); /* { dg-warning "argument 1 null where 
non-null expected because argument 3 is nonzero" } */
+  func1 (i1 ? NULL : cp1, cp2, 2); /* { dg-warning "argument 1 null where 
non-null expected because argument 3 is nonzero" } */
+  func1 (i1 ? (i2 ? cp1 : NULL) : cp2, cp3, 3); /* { dg-warning "argument 1 
null where non-null expected because argument 3 is nonzero" } */
+
+  func4 (0, NULL, NULL, 0);
+  func4 (-1, NULL, cp1, 0); /* { dg-warning "argument 2 null where non-null 
expected because argument 1 is nonzero" } */
+  func4 (0, cp1, NULL, 77); /* { dg-warning "argument 3 null where non-null 
expected because argument 4 is nonzero" } */
+}
diff --git a/gcc/testsuite/gcc.dg/nonnull-9.c b/gcc/testsuite/gcc.dg/nonnull-9.c
new file mode 100644
index 000000000000..c0f95ca66631
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/nonnull-9.c
@@ -0,0 +1,40 @@
+/* Test for the invalid use of the "nonnull_if_nonzero" function attribute.  */
+/* { dg-do compile } */
+/* { dg-options "-std=gnu17 -pedantic-errors" } */
+
+extern void func1 () __attribute__((nonnull_if_nonzero)); /* { dg-error "wrong 
number of arguments specified for 'nonnull_if_nonzero' attribute" } */
+/* { dg-message "expected 2, found 0" "" { target *-*-* } .-1 } */
+
+extern void func2 (char *) __attribute__((nonnull_if_nonzero(1))); /* { 
dg-error "wrong number of arguments specified for 'nonnull_if_nonzero' 
attribute" } */
+/* { dg-message "expected 2, found 1" "" { target *-*-* } .-1 } */
+
+extern void func3 (char *) __attribute__((nonnull_if_nonzero(1, 2, 3))); /* { 
dg-error "wrong number of arguments specified for 'nonnull_if_nonzero' 
attribute" } */
+/* { dg-message "expected 2, found 3" "" { target *-*-* } .-1 } */
+
+extern void func4 (char *, int) __attribute__((nonnull_if_nonzero(3, 2))); /* 
{ dg-warning "'nonnull_if_nonzero' attribute argument 1 value '3' exceeds the 
number of function parameters 2" } */
+
+extern void func5 (char *, int) __attribute__((nonnull_if_nonzero(1, 3))); /* 
{ dg-warning "nonnull_if_nonzero' attribute argument 2 value '3' exceeds the 
number of function parameters 2" } */
+
+extern void func6 (char *, int) __attribute__((nonnull_if_nonzero (foo, 2))); 
/* { dg-warning ".nonnull_if_nonzero. attribute argument 1 is invalid" } */
+/* { dg-error ".foo. undeclared" "undeclared argument" { target *-*-* } .-1 } 
*/
+
+extern void func7 (char *, int) __attribute__((nonnull_if_nonzero (1, bar))); 
/* { dg-warning ".nonnull_if_nonzero. attribute argument 2 is invalid" } */
+/* { dg-error ".bar. undeclared" "undeclared argument" { target *-*-* } .-1 } 
*/
+
+extern void func8 (int, int) __attribute__((nonnull_if_nonzero(1, 2))); /* { 
dg-warning "'nonnull_if_nonzero' attribute argument 1 value '1' refers to 
parameter type 'int'" } */
+
+extern void func9 (char *, float) __attribute__((nonnull_if_nonzero(1, 2))); 
/* { dg-warning "'nonnull_if_nonzero' attribute argument 2 value '2' refers to 
parameter type 'float'" } */
+
+extern void func10 (char *, _Bool) __attribute__((nonnull_if_nonzero(1, 2))); 
/* { dg-warning "'nonnull_if_nonzero' attribute argument 2 value '2' refers to 
parameter type '_Bool'" } */
+
+extern void func11 (char *, char *) __attribute__((nonnull_if_nonzero(1, 2))); 
/* { dg-warning "'nonnull_if_nonzero' attribute argument 2 value '2' refers to 
parameter type 'char \\\*'" } */
+
+void
+foo (void)
+{
+}
+
+void
+bar (void)
+{
+}
diff --git a/gcc/tree-ssa-ccp.cc b/gcc/tree-ssa-ccp.cc
index 21c5440c9a25..0c08ed62cda2 100644
--- a/gcc/tree-ssa-ccp.cc
+++ b/gcc/tree-ssa-ccp.cc
@@ -154,6 +154,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "ipa-cp.h"
 #include "ipa-prop.h"
 #include "internal-fn.h"
+#include "gimple-range.h"
 
 /* Possible lattice values.  */
 typedef enum
@@ -4546,6 +4547,7 @@ unsigned int
 pass_post_ipa_warn::execute (function *fun)
 {
   basic_block bb;
+  gimple_ranger *ranger = NULL;
 
   FOR_EACH_BB_FN (bb, fun)
     {
@@ -4557,14 +4559,15 @@ pass_post_ipa_warn::execute (function *fun)
            continue;
 
          tree fntype = gimple_call_fntype (stmt);
-         bitmap nonnullargs = get_nonnull_args (fntype);
-         if (!nonnullargs)
+         if (!fntype)
            continue;
+         bitmap nonnullargs = get_nonnull_args (fntype);
 
          tree fndecl = gimple_call_fndecl (stmt);
          const bool closure = fndecl && DECL_LAMBDA_FUNCTION_P (fndecl);
 
-         for (unsigned i = 0; i < gimple_call_num_args (stmt); i++)
+         for (unsigned i = nonnullargs ? 0 : ~0U;
+              i < gimple_call_num_args (stmt); i++)
            {
              tree arg = gimple_call_arg (stmt, i);
              if (TREE_CODE (TREE_TYPE (arg)) != POINTER_TYPE)
@@ -4612,8 +4615,67 @@ pass_post_ipa_warn::execute (function *fun)
                        fndecl, "nonnull");
            }
          BITMAP_FREE (nonnullargs);
+
+         for (tree attrs = TYPE_ATTRIBUTES (fntype);
+              (attrs = lookup_attribute ("nonnull_if_nonzero", attrs));
+              attrs = TREE_CHAIN (attrs))
+           {
+             tree args = TREE_VALUE (attrs);
+             unsigned int idx = TREE_INT_CST_LOW (TREE_VALUE (args)) - 1;
+             unsigned int idx2
+               = TREE_INT_CST_LOW (TREE_VALUE (TREE_CHAIN (args))) - 1;
+             if (idx < gimple_call_num_args (stmt)
+                 && idx2 < gimple_call_num_args (stmt))
+               {
+                 tree arg = gimple_call_arg (stmt, idx);
+                 tree arg2 = gimple_call_arg (stmt, idx2);
+                 if (TREE_CODE (TREE_TYPE (arg)) != POINTER_TYPE
+                     || !integer_zerop (arg)
+                     || !INTEGRAL_TYPE_P (TREE_TYPE (arg2))
+                     || integer_zerop (arg2)
+                     || ((TREE_CODE (fntype) == METHOD_TYPE || closure)
+                         && (idx == 0 || idx2 == 0)))
+                   continue;
+                 if (!integer_nonzerop (arg2)
+                     && !tree_expr_nonzero_p (arg2))
+                   {
+                     if (TREE_CODE (arg2) != SSA_NAME || optimize < 2)
+                       continue;
+                     if (!ranger)
+                       ranger = enable_ranger (cfun);
+
+                     int_range_max vr;
+                     get_range_query (cfun)->range_of_expr (vr, arg2, stmt);
+                     if (range_includes_zero_p (vr))
+                       continue;
+                   }
+                 unsigned argno = idx + 1;
+                 unsigned argno2 = idx2 + 1;
+                 location_t loc = (EXPR_HAS_LOCATION (arg)
+                                   ? EXPR_LOCATION (arg)
+                                   : gimple_location (stmt));
+                 auto_diagnostic_group d;
+
+                 if (!warning_at (loc, OPT_Wnonnull,
+                                  "argument %u null where non-null "
+                                  "expected because argument %u is "
+                                  "nonzero", argno, argno2))
+                   continue;
+
+                 tree fndecl = gimple_call_fndecl (stmt);
+                 if (fndecl && DECL_IS_UNDECLARED_BUILTIN (fndecl))
+                   inform (loc, "in a call to built-in function %qD",
+                           fndecl);
+                 else if (fndecl)
+                   inform (DECL_SOURCE_LOCATION (fndecl),
+                           "in a call to function %qD declared %qs",
+                           fndecl, "nonnull_if_nonzero");
+               }
+           }
        }
     }
+  if (ranger)
+    disable_ranger (cfun);
   return 0;
 }
 
diff --git a/gcc/tree.cc b/gcc/tree.cc
index 3ef1b6b483b3..74d13a8d5832 100644
--- a/gcc/tree.cc
+++ b/gcc/tree.cc
@@ -14785,7 +14785,7 @@ get_nonnull_args (const_tree fntype)
   /* A function declaration can specify multiple attribute nonnull,
      each with zero or more arguments.  The loop below creates a bitmap
      representing a union of all the arguments.  An empty (but non-null)
-     bitmap means that all arguments have been declaraed nonnull.  */
+     bitmap means that all arguments have been declared nonnull.  */
   for ( ; attrs; attrs = TREE_CHAIN (attrs))
     {
       attrs = lookup_attribute ("nonnull", attrs);
diff --git a/gcc/ubsan.cc b/gcc/ubsan.cc
index b858795aaf72..1a8faed6cc29 100644
--- a/gcc/ubsan.cc
+++ b/gcc/ubsan.cc
@@ -2046,8 +2046,9 @@ instrument_nonnull_arg (gimple_stmt_iterator *gsi)
   for (unsigned int i = 0; i < gimple_call_num_args (stmt); i++)
     {
       tree arg = gimple_call_arg (stmt, i);
+      tree arg2;
       if (POINTER_TYPE_P (TREE_TYPE (arg))
-         && infer_nonnull_range_by_attribute (stmt, arg))
+         && infer_nonnull_range_by_attribute (stmt, arg, &arg2))
        {
          gimple *g;
          if (!is_gimple_val (arg))
@@ -2057,6 +2058,13 @@ instrument_nonnull_arg (gimple_stmt_iterator *gsi)
              gsi_safe_insert_before (gsi, g);
              arg = gimple_assign_lhs (g);
            }
+         if (arg2 && !is_gimple_val (arg2))
+           {
+             g = gimple_build_assign (make_ssa_name (TREE_TYPE (arg2)), arg2);
+             gimple_set_location (g, loc[0]);
+             gsi_safe_insert_before (gsi, g);
+             arg2 = gimple_assign_lhs (g);
+           }
 
          basic_block then_bb, fallthru_bb;
          *gsi = create_cond_insert_point (gsi, true, false, true,
@@ -2068,6 +2076,18 @@ instrument_nonnull_arg (gimple_stmt_iterator *gsi)
          gsi_insert_after (gsi, g, GSI_NEW_STMT);
 
          *gsi = gsi_after_labels (then_bb);
+         if (arg2)
+           {
+             *gsi = create_cond_insert_point (gsi, true, false, true,
+                                              &then_bb, &fallthru_bb);
+             g = gimple_build_cond (NE_EXPR, arg2,
+                                    build_zero_cst (TREE_TYPE (arg2)),
+                                    NULL_TREE, NULL_TREE);
+             gimple_set_location (g, loc[0]);
+             gsi_insert_after (gsi, g, GSI_NEW_STMT);
+
+             *gsi = gsi_after_labels (then_bb);
+           }
          if (flag_sanitize_trap & SANITIZE_NONNULL_ATTRIBUTE)
            g = gimple_build_call (builtin_decl_explicit (BUILT_IN_TRAP), 0);
          else

Reply via email to