diff mbox series

run -Wnonnull later (PR 87489)

Message ID 1f4b76ee-067f-bb5e-1ed1-39286c5f7c75@gmail.com
State New
Headers show
Series run -Wnonnull later (PR 87489) | expand

Commit Message

Martin Sebor Feb. 1, 2021, 12:31 a.m. UTC
The initial -Wnonnull implementation in the middle end took place
too late in the pipeline (just before expansion), and as a result
was prone to false positives (bug 78817).  In an attempt to avoid
the worst of those, the warning was moved to the ccp2 pass in
r243874.  However, as the test case in PR 87489 shows, this is
in turn too early and causes other false positives as a result.

A few experiments with running the warning later suggest that
just before the mergephi2 pass is a good point to avoid this class
of false positives without causing any regressions or introducing
any new warnings either in Glibc or in Binutils/GDB.

Since PR 87489 is a GCC 8-11 regression I propose to make this
change on the trunk as well as on the release branches.

Martin

Comments

Martin Sebor Feb. 6, 2021, 5:14 p.m. UTC | #1
Ping:
   https://gcc.gnu.org/pipermail/gcc-patches/2021-February/564597.html

On 1/31/21 5:31 PM, Martin Sebor wrote:
> The initial -Wnonnull implementation in the middle end took place
> too late in the pipeline (just before expansion), and as a result
> was prone to false positives (bug 78817).  In an attempt to avoid
> the worst of those, the warning was moved to the ccp2 pass in
> r243874.  However, as the test case in PR 87489 shows, this is
> in turn too early and causes other false positives as a result.
> 
> A few experiments with running the warning later suggest that
> just before the mergephi2 pass is a good point to avoid this class
> of false positives without causing any regressions or introducing
> any new warnings either in Glibc or in Binutils/GDB.
> 
> Since PR 87489 is a GCC 8-11 regression I propose to make this
> change on the trunk as well as on the release branches.
> 
> Martin
Jeff Law Feb. 8, 2021, 11:15 p.m. UTC | #2
On 1/31/21 5:31 PM, Martin Sebor via Gcc-patches wrote:
> The initial -Wnonnull implementation in the middle end took place
> too late in the pipeline (just before expansion), and as a result
> was prone to false positives (bug 78817).  In an attempt to avoid
> the worst of those, the warning was moved to the ccp2 pass in
> r243874.  However, as the test case in PR 87489 shows, this is
> in turn too early and causes other false positives as a result.
>
> A few experiments with running the warning later suggest that
> just before the mergephi2 pass is a good point to avoid this class
> of false positives without causing any regressions or introducing
> any new warnings either in Glibc or in Binutils/GDB.
>
> Since PR 87489 is a GCC 8-11 regression I propose to make this
> change on the trunk as well as on the release branches.
>
> Martin
>
> gcc-87489.diff
>
> PR middle-end/87489 - Spurious -Wnonnull warning on bitwise arithmetic and inlining
>
> gcc/ChangeLog:
>
> 	PR middle-end/87489
> 	* passes.def (pass_post_ipa_warn): Move later.
>
> gcc/testsuite/ChangeLog:
>
> 	PR middle-end/87489
> 	* gcc.dg/Wnonnull-6.c: New test.
> 	* gcc.dg/Wnonnull-7.c: New test.
So moving passes is generally not the right solution to most problems --
it usually turns into a game of wack-a-mole.    So rather than looking
at pass reordering, let's look at the IL and see if there's reasonable
optimizations we could do that in turn would avoid the false positive. 
I mentioned this in c#11 in the BZ.

What I missed last year was that we could improve the backwards threader.


If we look at the forwprop1 dump we have this leading up to the
problematical strlen call:

;;   basic block 2, loop depth 0, maybe hot
;;    prev block 0, next block 3, flags: (NEW, VISITED)
;;    pred:       ENTRY (FALLTHRU,EXECUTABLE)
  j = 0;
  if (i_13(D) != 0)
    goto <bb 3>; [INV]
  else
    goto <bb 4>; [INV]
;;    succ:       3 (TRUE_VALUE,EXECUTABLE)
;;                4 (FALSE_VALUE,EXECUTABLE)

;;   basic block 3, loop depth 0, maybe hot
;;    prev block 2, next block 4, flags: (NEW, VISITED)
;;    pred:       2 (TRUE_VALUE,EXECUTABLE)
  j.0_1 = j;
  _2 = j.0_1 | 1;
  j = _2;
;;    succ:       4 (FALLTHRU,EXECUTABLE)

;;   basic block 4, loop depth 0, maybe hot
;;    prev block 3, next block 5, flags: (NEW, VISITED)
;;    pred:       2 (FALSE_VALUE,EXECUTABLE)
;;                3 (FALLTHRU,EXECUTABLE)
  j.1_3 = j;
  if (j.1_3 != 0)
    goto <bb 5>; [INV]
  else
    goto <bb 6>; [INV]
;;    succ:       5 (TRUE_VALUE,EXECUTABLE)
;;                6 (FALSE_VALUE,EXECUTABLE)

;;   basic block 5, loop depth 0, maybe hot
;;    prev block 4, next block 6, flags: (NEW, VISITED)
;;    pred:       4 (TRUE_VALUE,EXECUTABLE)
  f (&j, 0);
;;    succ:       6 (FALLTHRU,EXECUTABLE)

;;   basic block 6, loop depth 0, maybe hot
;;    prev block 5, next block 7, flags: (NEW, VISITED)
;;    pred:       4 (FALSE_VALUE,EXECUTABLE)
;;                5 (FALLTHRU,EXECUTABLE)
  j.2_4 = j;
  _5 = j.2_4 & 1;
  if (_5 != 0)
    goto <bb 7>; [INV]
  else
    goto <bb 8>; [INV]
;;    succ:       7 (TRUE_VALUE,EXECUTABLE)
;;                8 (FALSE_VALUE,EXECUTABLE)

Of particular interest is the fact that we always know where BB4 will
go.  If we take 2->3->4 then 4 will always transfer to 5.  If we take
2->4 then 4 will always transfer to 6.

The problem is the backwards threader is a bit dumb in that it only
allows a very limited number of RHS expressions.  So when it sees j.1_3
= j in BB4 it gives up.

If we fix that and use walk_aliased_vdefs we can pretty easily find the
j = 0 statement in BB2 and thread 2->4->6.  That in turn allows
subsequent optimizers to do a better job and the problematical call is gone.

While that is enough to fix the testcase and it helps clean up the code
in the original BZ a bit, we still get the warning.  We may need to
handle variants of this kind of code:
;;   basic block 4, loop depth 0, maybe hot
;;    prev block 3, next block 5, flags: (NEW, VISITED)
;;    pred:       2 (TRUE_VALUE,EXECUTABLE)
  _1 = xl_xinfo.xinfo;
  _2 = _1 | 16;
  xl_xinfo.xinfo = _2;
  xl_twophase.xid = twophase_xid_16(D);
  _3 = xl_xinfo.xinfo;
  if (_3 != 0)
    goto <bb 5>; [INV]
  else
    goto <bb 6>; [INV]
;;    succ:       5 (TRUE_VALUE,EXECUTABLE)
;;                6 (FALSE_VALUE,EXECUTABLE)

We can see pretty easily that _3 is never zero.  The backwards threader
can't make that deduction, but I  think I see how to add it pretty
easily.  The combination of those two extensions *should* allow the
backwards threader to clean this up much earlier in the pipeline and
avoid the warning.

Anyways, still poking around, but my general sense is that changing pass
ordering can be avoided here.

jeff
Franz Sirl Feb. 19, 2021, 9:48 a.m. UTC | #3
Am 2021-02-01 um 01:31 schrieb Martin Sebor via Gcc-patches:
> The initial -Wnonnull implementation in the middle end took place
> too late in the pipeline (just before expansion), and as a result
> was prone to false positives (bug 78817).  In an attempt to avoid
> the worst of those, the warning was moved to the ccp2 pass in
> r243874.  However, as the test case in PR 87489 shows, this is
> in turn too early and causes other false positives as a result.
> 
> A few experiments with running the warning later suggest that
> just before the mergephi2 pass is a good point to avoid this class
> of false positives without causing any regressions or introducing
> any new warnings either in Glibc or in Binutils/GDB.
> 
> Since PR 87489 is a GCC 8-11 regression I propose to make this
> change on the trunk as well as on the release branches.

Hi Martin,

I tested your patch and it showed also one more warning for this 
testcase with -O2 -Wnonnull:

class b {
public:
   long c();
};
class B {
public:
   B() : f() {}
   b *f;
};
long d, e;
class g : B {
public:
   void h() {
     long a = f->c();
     d = e = a;
   }
};
class j {
   j();
   g i;
};
j::j() { i.h(); }

I hope cvise didn't minimize too much, but at least in the original much 
larger code the warning looks reasonable too.

Franz
Martin Sebor Feb. 19, 2021, 6:25 p.m. UTC | #4
On 2/19/21 2:48 AM, Franz Sirl wrote:
> Am 2021-02-01 um 01:31 schrieb Martin Sebor via Gcc-patches:
>> The initial -Wnonnull implementation in the middle end took place
>> too late in the pipeline (just before expansion), and as a result
>> was prone to false positives (bug 78817).  In an attempt to avoid
>> the worst of those, the warning was moved to the ccp2 pass in
>> r243874.  However, as the test case in PR 87489 shows, this is
>> in turn too early and causes other false positives as a result.
>>
>> A few experiments with running the warning later suggest that
>> just before the mergephi2 pass is a good point to avoid this class
>> of false positives without causing any regressions or introducing
>> any new warnings either in Glibc or in Binutils/GDB.
>>
>> Since PR 87489 is a GCC 8-11 regression I propose to make this
>> change on the trunk as well as on the release branches.
> 
> Hi Martin,
> 
> I tested your patch and it showed also one more warning for this 
> testcase with -O2 -Wnonnull:
> 
> class b {
> public:
>    long c();
> };
> class B {
> public:
>    B() : f() {}
>    b *f;
> };
> long d, e;
> class g : B {
> public:
>    void h() {
>      long a = f->c();
>      d = e = a;
>    }
> };
> class j {
>    j();
>    g i;
> };
> j::j() { i.h(); }
> 
> I hope cvise didn't minimize too much, but at least in the original much 
> larger code the warning looks reasonable too.

Thanks.  Yes, the warning would be useful here since the f pointer
in the call to f->c() is null when i.h() is called from j's ctor.
The FRE3 pass clearly exposes this :

void j::j (struct j * const this)
{
   long int _9;

   <bb 2> [local count: 1073741824]:
   MEM[(struct B *)this_3(D)] ={v} {CLOBBER};
   MEM[(struct B *)this_3(D)].f = 0B;
   _9 = b::c (0B);
   ...

Because the warning runs early (after CCP2), the null pointer is
not detected.  To see it the warning code would have to work quite
hard to figure out that the two assignments below refer to the same
location (it would essentially have to do what FRE does):

   MEM[(struct B *)this_3(D)].f = 0B;
   _7 = MEM[(struct b * *)_1];
   _9 = b::c (_7);

It's probably too late to make this change for GCC 11 as Jeff has
already decided that it should be deferred until GCC 12, and even
then there's a concern that doing so might cause false positives.
I think it's worth revisiting the idea in GCC 12 to see if
the concern is founded.  Let me make a note of it in the bug.

Martin
diff mbox series

Patch

PR middle-end/87489 - Spurious -Wnonnull warning on bitwise arithmetic and inlining

gcc/ChangeLog:

	PR middle-end/87489
	* passes.def (pass_post_ipa_warn): Move later.

gcc/testsuite/ChangeLog:

	PR middle-end/87489
	* gcc.dg/Wnonnull-6.c: New test.
	* gcc.dg/Wnonnull-7.c: New test.

diff --git a/gcc/passes.def b/gcc/passes.def
index e9ed3c7bc57..5e5a012943a 100644
--- a/gcc/passes.def
+++ b/gcc/passes.def
@@ -194,7 +194,6 @@  along with GCC; see the file COPYING3.  If not see
 	 They ensure memory accesses are not indirect wherever possible.  */
       NEXT_PASS (pass_strip_predict_hints, false /* early_p */);
       NEXT_PASS (pass_ccp, true /* nonzero_p */);
-      NEXT_PASS (pass_post_ipa_warn);
       /* After CCP we rewrite no longer addressed locals into SSA
 	 form if possible.  */
       NEXT_PASS (pass_complete_unrolli);
@@ -207,6 +206,7 @@  along with GCC; see the file COPYING3.  If not see
       NEXT_PASS (pass_build_alias);
       NEXT_PASS (pass_return_slot);
       NEXT_PASS (pass_fre, true /* may_iterate */);
+      NEXT_PASS (pass_post_ipa_warn);
       NEXT_PASS (pass_merge_phi);
       NEXT_PASS (pass_thread_jumps);
       NEXT_PASS (pass_vrp, true /* warn_array_bounds_p */);
@@ -368,7 +368,6 @@  along with GCC; see the file COPYING3.  If not see
       NEXT_PASS (pass_lower_switch);
       /* Perform simple scalar cleanup which is constant/copy propagation.  */
       NEXT_PASS (pass_ccp, true /* nonzero_p */);
-      NEXT_PASS (pass_post_ipa_warn);
       NEXT_PASS (pass_object_sizes);
       /* Fold remaining builtins.  */
       NEXT_PASS (pass_fold_builtins);
@@ -377,6 +376,7 @@  along with GCC; see the file COPYING3.  If not see
          to forward object-size and builtin folding results properly.  */
       NEXT_PASS (pass_copy_prop);
       NEXT_PASS (pass_dce);
+      NEXT_PASS (pass_post_ipa_warn);
       NEXT_PASS (pass_sancov);
       NEXT_PASS (pass_asan);
       NEXT_PASS (pass_tsan);
diff --git a/gcc/testsuite/gcc.dg/Wnonnull-6.c b/gcc/testsuite/gcc.dg/Wnonnull-6.c
new file mode 100644
index 00000000000..507e7979cd0
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wnonnull-6.c
@@ -0,0 +1,25 @@ 
+/* PR middle-end/87489 - Spurious -Wnonnull warning on bitwise arithmetic
+   and inlining
+   { dg-do compile }
+   { dg-options "-O1 -Wall" } */
+
+extern void f (const void*, int);
+
+static void g (int i, const char *s)
+{
+  int j = 0;
+
+  if (i)
+    j |= 1;
+
+  if (j)
+    f (&j, 0);
+
+  if (j & 1)
+    f (s, __builtin_strlen (s));    // { dg-bogus "\\\[-Wnonnull" }
+}
+
+void h (void)
+{
+  g (0, 0);
+}
diff --git a/gcc/testsuite/gcc.dg/Wnonnull-7.c b/gcc/testsuite/gcc.dg/Wnonnull-7.c
new file mode 100644
index 00000000000..d801ca2329d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wnonnull-7.c
@@ -0,0 +1,6 @@ 
+/* PR middle-end/87489 - Spurious -Wnonnull warning on bitwise arithmetic
+   and inlining
+   { dg-do compile }
+   { dg-options "-Og -Wall" } */
+
+#include "Wnonnull-6.c"