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. 2013-10-08 Marc Glisse <marc.gli...@inria.fr> PR tree-optimization/58480 gcc/ * tree-vrp.c (infer_value_range): Look for a nonnull attribute. 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: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 203229) +++ tree-vrp.c (working copy) @@ -4493,28 +4493,58 @@ infer_value_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) { 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) + goto nonnull; + + if (gimple_code (stmt) == GIMPLE_CALL) { - *val_p = build_int_cst (TREE_TYPE (op), 0); - *comp_code_p = NE_EXPR; - return true; + 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) + goto nonnull; + + /* 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) + goto nonnull; + } + } } } return false; + +nonnull: + *val_p = build_int_cst (TREE_TYPE (op), 0); + *comp_code_p = NE_EXPR; + return true; } void dump_asserts_for (FILE *, tree); void debug_asserts_for (tree); void dump_all_asserts (FILE *); void debug_all_asserts (void); /* Dump all the registered assertions for NAME to FILE. */