On Tue, 8 Feb 2022, Michael Matz wrote: > Hello, > > On Tue, 8 Feb 2022, Richard Biener wrote: > > > int foo(int always_true_at_runtime) > > { > > { > > int *p; > > if (always_true_at_runtime) > > goto after; > > lab: > > return *p; > > after: > > int i = 0; > > p = &i; > > if (always_true_at_runtime) > > goto lab; > > } > > return 0; > > } > > > > For the implementation I considered starting lifetime at a DECL_EXPR > > if it exists and otherwise at the start of the BIND_EXPR. Note the > > complication is that control flow has to reach the lifetime-start > > clobber before the first access. But that might also save us here, > > since for the example above 'i' would be live via the backedge > > from goto lab. > > > > That said, the existing complication is that when the gimplifier > > visits the BIND_EXPR it has no way to know whether there will be > > a following DECL_EXPR or not (and the gimplifier notes there are > > cases a DECL_EXPR variable is not in a BIND_EXPR). My plan is to > > compute this during one of the body walks the gimplifier performs > > before gimplifying. > > > > Another complication is that at least the C frontend + gimplifier > > combination for > > > > switch (i) > > { > > case 1: > > int i; > > break; > > } > > > > will end up having the BIND_EXPR mentioning 'i' containing the > > case label, so just placing the birth at the BIND will make it > > unreachable as > > > > i = BIRTH; > > case_label_for_1: > > ... > > I think anything that needs to happen (conceptually) during the jump from > switch to case-label needs to happen right before the jump, that might > mean creating a new fake BLOCK that contains just the jump and the > associated births. > > > conveniently at least the C frontend has a DECL_EXPR for 'i' > > which avoids this and I did not find a way (yet) in the gimplifier > > to rectify this when gimplifying switches. > > In C a 'case' label is nothing else than a normal label, it doesn't start > it's own block or the like. So also for that one the births would need to > be at the start of their containing blocks. > > > So there's still work to do but I think that starting the lifetime at a > > DECL_EXPR if it exists is the way to go? > > Depends on where the DECL_EXPR is exactly placed, but probably it wouldn't > be okay. You can't place the birth at the fall-through path, because this > needs to work (basically your jump example above rewritten as switch): > > int state = 2, *p, camefrom1 = 0; > for (;;) switch (state) { > case 1: > case 2: ; > int i; > if (state != 1) { p = &i; i = 0; } > if (state == 1) { (*p)++; return *p; } > state = 1; > continue; > } > > Note how i is initialized during state 2, and needs to be kept initialized > during state 1, so there must not be a CLOBBER (birth or other) at the > point of the declaration of 'i'. AFAICS in my simple tests a DECL_EXPR > for 'i' is placed with the statement associated with 'case 2' label, > putting a CLOBBER there would be the wrong thing. If the decl had an > initializer it might be harmless, as it would be overwritten at that > place, but even so, in this case there is no initializer. Hmm.
You get after gimplification: state = 2; camefrom1 = 0; <D.1989>: switch (state) <default: <D.1996>, case 1: <D.1983>, case 2: <D.1984>> { int i; try { i = {CLOBBER(birth)}; /// ignore, should go away <D.1983>: <D.1984>: i = {CLOBBER(birth)}; if (state != 1) goto <D.1991>; else goto <D.1992>; <D.1991>: p = &i; i = 0; <D.1992>: if (state == 1) goto <D.1993>; else goto <D.1994>; <D.1993>: _1 = *p; _2 = _1 + 1; *p = _2; D.1995 = *p; // predicted unlikely by early return (on trees) predictor. return D.1995; <D.1994>: state = 1; // predicted unlikely by continue predictor. goto <D.1987>; } finally { i = {CLOBBER(eol)}; } } which I think is OK? That is, when the abstract machine arrives at 'int i;' then the previous content in 'i' goes away? Or would int foo() { goto ick; use: int i, *p; return *p; ick: i = 1; p = &i; goto use; } require us to return 1? With the current patch 'i' is clobbered before the return. > Another complication arises from simple forward jumps: > > goto forw; > { > int i; > printf("not reachable\n"); > forw: > i = 1; > } > > Despite the jump skiping the unreachable head of the BLOCK, 'i' needs to > be considered birthed at the label. (In a way the placement of births > exactly mirrors the placements of deaths (of course), every path from > outside a BLOCK to inside needs to birth-clobber all variables (in C), > like every path leaving needs to kill them. It's just that we have a > convenient construct for the latter (try-finally), but not for the former) The situation with an unreachable birth is handled conservatively since we consider a variable without a (visible at RTL expansion time) birth to conflict with all other variables. I don't see a good way to have a birth magically appear at 'forw' without trying to argue that the following stmt is the first mentioning the variable. Richard.