[LLVMdev] Question about implementing exceptions, especially to the VMKit team

Kevin Modzelewski kmod at dropbox.com
Fri May 2 16:37:20 PDT 2014


That's definitely good confirmation to hear that the test+branch for every
call does in fact add noticeable overhead -- thanks for the datapoints.

What I'm taking away from this is that even within the space of
"unwind-based exception handling using DWARF CFI side-tables", there is a
fair amount of room for different approaches with different tradeoffs, and
also potentially room for a custom-tailored unwinder to beat libgcc.
 That's definitely good to know, and you guys have encouraged me to peel
back the magic one more layer and try to implement my own unwinder :)

As for switching between unwind-based exceptions and checked-status-code
exceptions, I'm not quite sure I buy that that can completely be done by
the catching function, since the throwing function also needs to use the
matching mechanism.  I think if you truly want to do this, you need to
compile separate variants of whatever functions you might call (including
whatever functions they might call), one for each exception mechanism you
want to use.  I'm thinking about doing this, but only for certain built-in
functions that are expected to throw a lot.  Another option I'm thinking of
is to inline those particular functions and then create an optimization
pass that will know that py_throw always throws, and stitch up the CFG
appropriately.  Anyway, lots to chew on, thanks everyone for the responses!



Aside about Python exceptions: Python has interesting for loops, which are
always for-each loops and implement the termination condition using
exceptions:

PyObject *iterator; // what we're iterating over
while (true) {
    PyObject* i;
    try {
        i = iterator.next();
    } except (StopIteration) {
        break;
    }
    // do stuff
}

Percentage-wise, throwing the StopIteration might be rare, but I would
wager that most loops get terminated this way (as opposed to a "break"
statement) so it's certainly not never; I think this means the exception
gets thrown enough that it's better to handle the exception in-line rather
than do a deopt-on-throw.  Microbenchmarks suggest that for-loop overhead
is important enough that it's further worth trying to avoid any
exception-related unwinding entirely, but I'm not sure how true that is for
larger programs (probably somewhat true).

kmod


On Fri, May 2, 2014 at 12:43 PM, Sanjoy Das <sanjoy at azulsystems.com> wrote:

> Hi Kevin,
>
> To elaborate on Philip's point, depending on the state Pyston's
> runtime already is in, you may have the choice of using a hybrid of a
> "pending exception" word in your runtime thread structure, and an
> implicit alternate ("exceptional") return address for calls into
> functions that may throw.  This lets you elide the check on the
> pending exception word after calls by turning them into invokes that
> unwind into a landingpad containing a generic exception handler.  This
> generic exception handler then checks the type of the pending
> exception word and handles the exception (which may involve rethrowing
> to the caller if the current frame doesn't have catch handler).
>
> Instead of relying on libgcc to unwind when you throw you can then
> parse the [call PC, generic exception handling PC] pairs from the
> .eh_frame section, and when throwing to your caller, look up the
> generic exception handling PC (using the call PC pushed on the stack)
> and "return" to that instead.  Rethrow is similar.
>
> This scheme has the disadvantage of "returning" through every active
> frame on an exception throw, even if a particular frame never had an
> exception handler and could've been skipped safely.  However, this
> scheme allows you to easily switch to one of two other implementations
> based on profiling data on a per-callsite basis:
>
>  1. high exception volume -- if an invoke has seen too many exception
>     throws, recompile by replacing the invoke with a call followed by
>     a test of "pending exception" and branch.  The logic to generate
>     the branch target should largely be the same as logic to generate
>     the landing pad block.
>
>  2. low exception volume -- keep the invoke, but put a deoptimization
>     trap in the landing pad block.
>
> We did some rough benchmarking, and using such implicit exceptions
> (i.e. not explicitly checking the pending exception word) reduces
> non-throwing call overhead by 20-25%.  I don't have any numbers on how
> it affects the performance of exceptional control flow though.
>
> -- Sanjoy
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20140502/1f332f0d/attachment.html>


More information about the llvm-dev mailing list