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

--- Comment #8 from Richard Biener <rguenth at gcc dot gnu.org> ---
(In reply to Michael Matz from comment #7)
> No, this is not a problem in the stack slot sharing algorithm, but rather in
> the input.  As presented to expand, and only showing the important parts,
> and reordering some BBs to make the flow more obvious:
> 
> ;;   basic block 2, loop depth 0
> ;;    pred:       ENTRY
>   _30 = (unsigned long) &in;
>   ivtmp.27_29 = _30 + 1;
>   goto <bb 5>; [100.00%]
> 
> So, 'in' becomes "live" here, it's address in _30 and _29.  Fallthrough to
> bb5,
> which also uses in, but otherwise is uninteresting, except that it can reach
> only BBs 6 and 7:
> 
> ;;   basic block 5, loop depth 1
>   ...
>   _2 = check_zero (&in, _31);
>   if (_2 != 0)
>     goto <bb 7>; [99.96%]
>   else
>     goto <bb 6>; [0.04%]
> 
> bb6 is a no-return block, hence uninteresting.  bb7 _is_ interesting in that
> it clobbers in:
> 
> ;;   basic block 7, loop depth 1
> ;;    pred:       5
>   in ={v} {CLOBBER};
>   if (i_11 != 5)
>     goto <bb 8>; [83.33%]
>   else
>     goto <bb 9>; [16.67%]
> 
> Note that the semantics of the clobber is not only that the former contents
> are destroyed, but rather that the very storage associated with the clobbered
> entity is gone.  So, from now on, any pointers into 'in', and memory accesses
> into 'in' are invalid.  Nevertheless the flow from bb7 goes to bb 8 and 9,
> the latter being the return block, so:
> 
> ;;   basic block 8, loop depth 1
> ;;    pred:       7
>   if (i_11 > 0)
>     goto <bb 3>; [100.00%]
>   else
>     goto <bb 4>; [0.00%]
> 
> and here we finally have a path into bb3, which is the other interesting one:
> 
> ;;   basic block 3, loop depth 2
>   # ivtmp.20_6 = PHI <_30(8), ivtmp.20_18(3)>
> 
> .... BOOM! .... Here _30 is used, and ...
> 
>   _4 = (void *) ivtmp.20_6;
>   MEM[base: _4, offset: 0B] = 0;
> 
> ... even written into ...  That's invalid.  _30 is associated with an
> object that is clobbered and gone ...
> 
>   set_one (&buf);
>   buf ={v} {CLOBBER};
>   ivtmp.20_18 = ivtmp.20_6 + 1;
> 
> ... and as the MEM[] write can't have possibly been into 'in' (as that is
> invalid, as 'in' didn't exist at the MEM access), it's okay and sensible to
> allocate 'in' and 'buf' into the same memory.
> 
> It seems to be a common misconception of what the clobber is really about.
> I designed it to mean what I wrote above, the storage associated with the
> clobbered entities is gone after the clobber (not merely it's former
> contents!). 
> 
> But ivopts or dom extends the lifetime of 'in' (by moving an address-taken
> earlier) and hence the lifetime of its storage, but without doing anything
> about the clobber (and hence not _really_ extending the lifetime).  That
> doesn't work.
> It's basically a mirrored transformation of the usual take-address-of-local
> and access it out of it's declared scope, just that here the _start_ of the
> supposed lifetime is moved out of the declared scope, not the end.

While I spotted this "issue" as well I respectfully disagree about
the semantics.  Not because they are not sound but because of
the implementation challenge resolving around address-takens being
values with no data dependence on the clobbers.

I thought the liveness algorithm would be able to handle this situation.
If not we need to prune clobbers that became "invalid" somehow.
Not sure how - the address value never "dies", so we'd need to compute
liveness of the things the address escapes to - SSA names are easy,
calls - well, we have to give up.  This possiby defeats the whole idea
of doing stack sharing analysis (I remember kernel/fortran testcases
passing the stack slots by reference to a function).

Now if we would want to try forcing the original semantics we need a
verifier verifying the IL state against bogus transforms - but then we
would have a way to prune the invalid ones.

Reply via email to