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. 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. 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. 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? Jakub