https://gcc.gnu.org/bugzilla/show_bug.cgi?id=90191

Jeffrey A. Law <law at redhat dot com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
             Status|UNCONFIRMED                 |RESOLVED
                 CC|                            |law at redhat dot com
         Resolution|---                         |INVALID

--- Comment #4 from Jeffrey A. Law <law at redhat dot com> ---
Actually I think the warning is valid.  Ramping up the aggressiveness of the
threader is what ultimately exposes it.

This code...

    m = a ? t::u() : 0;
    n.l = m;

Note how we can store 0 into "m" and we then store that into n.l.  Which is
then read by these statements:

    char &e = d[0];
    printf("%s = %s\n", &r, &e);

And thus we have a path where e can potentially be NULL.

This can be seen in the IPA inlining pass:

 <bb 2> [local count: 1073741824]:
  MEM[(struct  &)&d] ={v} {CLOBBER};
  _6 = x::y ();
  MEM[(struct  &)&d] ={v} {CLOBBER};
  if (_6 != 0)
    goto <bb 3>; [33.00%]
  else
    goto <bb 4>; [67.00%]

  <bb 3> [local count: 354334802]:
  _7 = t::u ();

  <bb 4> [local count: 1073741824]:
  # iftmp.1_8 = PHI <0B(2), _7(3)>
  m = iftmp.1_8;
  MEM[(struct v *)&d].n.l = iftmp.1_8;
  s.0_9 = s;
  ad<int, int, int, i> (0, 0, s.0_9, D.2603);
  goto <bb 8>; [100.00%]

  <bb 5> [count: 0]:
<L2>:
  c_10 = MEM[(struct v *)&d].n.l;
  if (c_10 != 0B)
    goto <bb 6>; [0.00%]
  else
    goto <bb 7>; [0.00%]

  <bb 6> [count: 0]:
  *c_10 ={v} {CLOBBER};
  operator delete (c_10, 1);

  <bb 7> [count: 0]:
  MEM[(struct  &)&d] ={v} {CLOBBER};
  resx 2

  <bb 8> [local count: 1073741824]:
  _3 = MEM[(char * *)&d];
  printf ("%s = %s\n", &r, _3);

See how bb4 stores iftmp.1_8 into d.n.l.  iftmp.1_8 when reached from block 2
has the value NULL.

Then in bb8 we read that value and pass it to printf.

Of course in the form above, the warning isn't going to fire.

In .mergephi2 we have (unnecessary stuff skipped):

;;   basic block 2, loop depth 0
;;    pred:       ENTRY
  MEM[(struct  &)&d] ={v} {CLOBBER};
  _6 = x::y ();
  MEM[(struct  &)&d] ={v} {CLOBBER};
  if (_6 != 0)
    goto <bb 3>; [33.00%]
  else
    goto <bb 4>; [67.00%]
;;    succ:       3
;;                4

;;   basic block 3, loop depth 0
;;    pred:       2
  _7 = t::u ();
;;    succ:       4

;;   basic block 4, loop depth 0
;;    pred:       2
;;                3
  # iftmp.1_8 = PHI <0B(2), _7(3)>
  m = iftmp.1_8;
  MEM[(struct v *)&d].n.l = iftmp.1_8;
  s.0_9 = s;
  ad<int, int, int, i> (0, 0, s.0_9, D.2603);
  goto <bb 8>; [100.00%]
;;    succ:       8
;;                5

;;   basic block 5, loop depth 0
;;    pred:       4
<L2>:
  if (iftmp.1_8 != 0B)
    goto <bb 6>; [0.00%]
  else
    goto <bb 7>; [0.00%]
;;    succ:       6
;;                7

;;   basic block 6, loop depth 0
;;    pred:       5
  *iftmp.1_8 ={v} {CLOBBER};
  operator delete (iftmp.1_8, 1);
;;    succ:       7

;;   basic block 7, loop depth 0
;;    pred:       5
;;                6
  MEM[(struct  &)&d] ={v} {CLOBBER};
  resx 2

Note carefully we have an EH edge (4,5).

The threader sees that the path 2->4->5 will always transfer control to bb7. 
So it's going to isolate path that by copying bb4.

THe block #s change a lot, but after ethread1 the relevant blocks:

;;   basic block 2, loop depth 0
;;    pred:       ENTRY
  MEM[(struct  &)&d] ={v} {CLOBBER};
  _6 = x::y ();
  MEM[(struct  &)&d] ={v} {CLOBBER};
  if (_6 != 0)
    goto <bb 5>; [33.00%]
  else
    goto <bb 3>; [67.00%]
;;    succ:       5
;;                3

;;   basic block 3, loop depth 0
;;    pred:       2 
  # iftmp.1_10 = PHI <0B(2)>
  m = iftmp.1_10;
  MEM[(struct v *)&d].n.l = iftmp.1_10;
  s.0_30 = s;     
  ad<int, int, int, i> (0, 0, s.0_30, D.2603);
  goto <bb 9>; [100.00%] 
;;    succ:       9
;;                4

;;   basic block 4, loop depth 0
;;    pred:       3
<L3>: 
  goto <bb 8>; [100.00%]
;;    succ:       8

;;   basic block 5, loop depth 0
;;    pred:       2
  _7 = t::u ();
  m = _7;
  MEM[(struct v *)&d].n.l = _7;
  s.0_9 = s;
  ad<int, int, int, i> (0, 0, s.0_9, D.2603);
  goto <bb 9>; [100.00%]
;;    succ:       9
;;                6
[ ... ]

;;   basic block 9, loop depth 0
;;    pred:       5
;;                3
  # iftmp.1_31 = PHI <_7(5), iftmp.1_10(3)>
  printf ("%s = %s\n", &r, iftmp.1_31);

Note how bb3 and bb5 have the same tail statements.  But they feed values into
the printf in bb9, in particular a NULL value for the path 2->3->9.  It becomes
more obvious after the VRP constant propagation:

;;   basic block 2, loop depth 0
;;    pred:       ENTRY
  MEM[(struct  &)&d] ={v} {CLOBBER};
  _6 = x::y ();   
  MEM[(struct  &)&d] ={v} {CLOBBER};
  if (_6 != 0)
    goto <bb 5>; [33.00%]
  else            
    goto <bb 3>; [67.00%]
;;    succ:       5
;;                3

;;   basic block 3, loop depth 0
;;    pred:       2 
  m = 0B;
  MEM[(struct v *)&d].n.l = 0B;
  s.0_30 = s;
  ad<int, int, int, i> (0, 0, s.0_30, D.2603);
  goto <bb 9>; [100.00%]
;;    succ:       9
;;                4

;;   basic block 4, loop depth 0
;;    pred:       3 
<L3>:
  goto <bb 8>; [100.00%]
;;    succ:       8

;;   basic block 5, loop depth 0
;;    pred:       2
  _7 = t::u ();
  m = _7;
  MEM[(struct v *)&d].n.l = _7;
  s.0_9 = s;
  ad<int, int, int, i> (0, 0, s.0_9, D.2603);
  goto <bb 9>; [100.00%]
;;    succ:       9
;;                6

;;   basic block 6, loop depth 0
;;    pred:       5
<L2>:
  if (_7 != 0B)
    goto <bb 7>; [0.00%]
  else
    goto <bb 8>; [0.00%]
;;    succ:       7
;;                8

;;   basic block 7, loop depth 0
;;    pred:       6
  *_7 ={v} {CLOBBER};
  operator delete (_7, 1);
;;    succ:       8

;;   basic block 8, loop depth 0
;;    pred:       6
;;                7
;;                4
  MEM[(struct  &)&d] ={v} {CLOBBER};
  resx 2
;;    succ:

;;   basic block 9, loop depth 0
;;    pred:       5
;;                3
  # iftmp.1_31 = PHI <_7(5), 0B(3)>
  printf ("%s = %s\n", &r, iftmp.1_31);
;;    succ:       10
;;                13

;;   basic block 10, loop depth 0
;;    pred:       9
  if (iftmp.1_31 != 0B)
    goto <bb 11>; [53.47%]
  else
    goto <bb 12>; [46.53%]

Note I've included bb10.  An astute observer would now realize that we've
exposed a secondary jump threading opportunity.  That's going to result in bb9
getting duplicated so that we can optimize away the test in bb10.

The key blocks in .thread2:

;;   basic block 9, loop depth 0
;;    pred:       3
  # iftmp.1_19 = PHI <0B(3)>
  printf ("%s = %s\n", &r, iftmp.1_19);
  goto <bb 13>; [100.00%]
;;    succ:       13
;;                14

;;   basic block 10, loop depth 0
;;    pred:       5
  # iftmp.1_31 = PHI <_7(5)>
  printf ("%s = %s\n", &r, iftmp.1_31);
;;    succ:       11
;;                15

bb9 being the one that's going to trigger the warning later.

But AFAICT the warning is 100% totally valid.  You just have to turn up the
optimizer thresholds to expose it.

Reply via email to