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) ∈ > 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.