[LLVMdev] RFC: How to represent SEH (__try / __except) in LLVM IR

Reid Kleckner rnk at google.com
Wed Dec 3 12:55:56 PST 2014


On Tue, Dec 2, 2014 at 6:05 PM, Vadim Chugunov <vadimcn at gmail.com> wrote:

> Sure, but memory access violations are not the only source of asynchronous
> exceptions.  There are also stack overflows, integer overflows (on
> platforms that support that in hardware), signaling NaNs, and so on...
>
> I am curious, what do you think of the following idea: what if instead of
> creating landing pads for running destructors, there were intrinsics for
> marking start and end of the object's lifetime (along with a pointer to
> destructor)?  LLVM could then emit a table of live objects for each PC
> range into LSDA, and the personality routine would interpret that table and
> invoke destructors.  (catch() would still need a landing pad, of course).
> /end bike-shedding
>

I don't think calls to start / end are good enough, because graphs don't
have scope. We are seeing problems with lifetime start / end already.
Consider a transformation which turns every branch into a branch to a
single basic block which switches over all possible branch targets. This is
a valid LLVM transformation, even if it is not an optimization, and it
would be impossible to recover the natural scope-like information from
start / end call pairs.

I also think that recovering from async exceptions will always be best
effort. The kinds of exceptions you describe are essentially results of
undefined behavior in LLVM IR, and can't be handled reliably. Unless we
introduce specific constructs with defined behavior (trapping integer
divide, trapping FP ops, trapping alloca), it will never work.

Chris Lattner had a proposal from a long time ago to add 'unwind' labels to
every basic block, but it introduces a lot of implicit control flow, which
we don't like:
http://nondot.org/sabre/LLVMNotes/ExceptionHandlingChanges.txt

You would do this:
  %p = call i8* malloc(i32 4)
  %xp = bitcast i8* %p to i32*
  ...

mybb: unwind to label %lpad1
  %x = load i32* %xp  ; edge to lpad1 here
  store i32 0, i32* %xp ; edge to lpad1 here
  call void @f() ; edge to lpad1 here
  br label %mybb2 ; cannot remove branch due to differing lpads

mybb2: unwind to label %lpad2
  ...

lpad:
  %xx = load i32* %xp ; we cannot make %xx a phi between %x and 0 due to
implicit control flow. Maybe we could split mybb and then make the phi,
but, ew.

This is a mountain. I think you can climb it, but I'm *not* signing up for
it. :) Adding and invoking intrinsics for all possibly trapping operations
seems much more tractable. Simply outlining try bodies is even easier.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20141203/291cfd82/attachment.html>


More information about the llvm-dev mailing list