On 2/22/22 11:56, Jakub Jelinek wrote:
On Tue, Feb 22, 2022 at 11:39:41AM -0500, Andrew MacLeod wrote:
I'd like to get clarification on some subtle terminology. I find I am
conflating calls that don't return with calls that may throw, and I think
they have different considerations.
My experiments with calls that can throw indicate that they always end a
basic block. This makes sense to me as there is the outgoing fall-thru edge
and an outgoing EH edge. Are there any conditions under which this is not
the case? (other than non-call exceptions)
Generally, there are 2 kinds of calls that can throw, those that can throw
internally and those can throw externally (e.g. there are
stmt_could_throw_{in,ex}ternal predicates).
Consider e.g.
void foo ();
struct S { S (); ~S (); };
void bar () { foo (); foo (); }
void baz () { S s; foo (); foo (); }
void qux () { try { foo (); } catch (...) {} }
the calls to foo in bar throw externally, if they throw, execution doesn't
continue anywhere in bar but in some bar's caller, or could just terminate
if nothing catches it at all. Such calls don't terminate a bb.
This is not a problem.
In baz, the s variable needs destruction if either of the foo calls throw,
so those calls do terminate bb and there are normal fallthru edges from
those bbs and eh edges to an EH pad which will destruct s and continue
propagating the exception.
In qux, there is explicit try/catch, so again, foo throws internally, ends
bb, has an EH edge to EH landing pad which will do what catch does.
Those also are not a problem, everything should flow fine in these
situations as well now that we make non-null adjustments on edges, and
don't for EH edges.
As far as these patches go, any block which has a call at the exit point
will not have any import or exports as there is no range stmt at the end
of the block, so we will not be marking anything in those blocks as stale.
That is EH, then there are calls that might not return because they leave
in some other way (e.g. longjmp), or might loop forever, might exit, might
abort, trap etc.
Generally speaking, calls which do not return should not now be a
problem... as long as they do not transfer control to somewhere else in
the current function.
I must say I don't know if we have any call flags that would guarantee
the function will always return (exactly once) if called.
Perhaps ECF_CONST/EFC_PURE without ECF_LOOPING_CONST_OR_PURE do?
I don't think I actually need that.
Andrew