[LLVMdev] Nested exception handlers

Talin viridia at gmail.com
Sat May 30 02:11:24 PDT 2009


All right, that is what I needed to know. Thanks very much!

I decided to go with _Unwind_Resume for the moment, just to get 
something up and running, since that option is by far the easiest to 
implement - I can always optimize later if needed. With that change, 
nested try blocks are working :)

Thanks for the warning about _Unwind_Resume. Fortunately, it appears 
that my code was already correct in this regard, although that was just 
an accident.

For cleanups, my plan is as follows: As I generate the CFG, I maintain a 
stack of cleanup handlers. Each time I generate a return, break, 
continue, or other statement that leaves a block, I insert a special 
"local call" instruction (one of my instructions, not LLVM IR) for each 
currently active cleanup handler. The cleanup handler ends with a "local 
return" block terminator. After the CFG is generated, I will run a 
transform over the CFG that converts all local calls into "set a state 
variable and jump", and all local returns into a switch on that variable.

Of course, what would be even more efficient is if labels could be 
first-class values :)

Duncan Sands wrote:
> Hi Talin,
>
>   
>> Since llvm-gcc is a rather large code base, which I have never looked at 
>> (or even run), could you give me a starting point of where to look?
>>     
>
> I meant: compile some nested C++ with llvm-gcc to see what it does.
> Otherwise, look in llvm-convert.cpp, especially EmitLandingPads.
>
>   
>> One thing I'd be interested in knowing is whether the 
>> llvm.eh.exception() intrinsic can be called more than once in a landing pad.
>>     
>
> I committed some stuff a week or so ago which means that you can now
> call llvm.eh.exception as many times as you like, from wherever you
> like, and get the right result.
>
>   
>> Say for example I have a nested try block, so that there are two landing 
>> pads, one for the inner try block, and one for the outer. Let's say that 
>> the inner landing pad has a "finally" block - a cleanup handler. This 
>> means that the inner try block must catch every exception type so that 
>> it can execute the cleanup even if there was no specific catch handler 
>> for that exception type.
>>
>> At the end of the finally block, we need to jump to various different 
>> destinations depending on how the finally block was entered - return, 
>> fall through, throw, etc. In the case where we failed to catch an 
>> exception in the inner try block, but the outer try block has a catch 
>> handler for that exception, the finally block needs to jump to the outer 
>> landing pad. There are three ways this could happen:
>>
>> 1) Call _Unwind_Resume, using an "invoke" IR instruction whose unwind 
>> block points to the outer landing pad. This seems like it would be 
>> expensive however.
>>     
>
> You need to be very careful with _Unwind_Resume.  Due to a change in
> libgcc (starting from gcc-4.3) you can only use _Unwind_Resume on an
> exception if it didn't match anything (i.e. you can use it only if you
> matched a "cleanup").  This is extremely annoying.
>
>   
>> 2) Branch directly to the start of the outer landing pad, and restart 
>> the exception dispatch all over. The problem with this is that the outer 
>> landing pad calls llvm.eh.exception() and llvm.eh.selector(), and I 
>> don't know if it's valid to call them at this point. Normally you don't 
>> jump to a landing pad directly, you get there via the personality 
>> function forcing a jump to that label. I don't know whether it is legal 
>> for the inner landing pad to jump to the start of the outer landing pad.
>>     
>
> LLVM doesn't yet support calling llvm.eh.selector far away from a
> landing pad, or multiple times.  This is something I would like to
> support but it is hard.
>
>   
>> 3) Branch directly to the individual catch blocks in the outer landing 
>> pad. In order to know which catch block to branch to, we need to add 
>> additional selectors to the inner landing pad representing the possible 
>> catch blocks in the outer landing pad that might catch the inner exception.
>>     
>
> This is what llvm-gcc does, if I understand you right.
>
>   
>> The problem with this scheme is that now the liveness of the current 
>> exception object is all tangled up.  The jump from the inner landing pad 
>> to the outer catch block passes through one or more cleanup blocks, each 
>> of which ends with a conditional jump. Each cleanup block has multiple 
>> predecessors, each predecessor setting a state variable telling the 
>> cleanup block where to jump to after finishing the cleanup. For a given 
>> cleanup block, "return" might set state = 0, "fall through" = 1, "catch 
>> block 1" = 2, "catch block 2" = 3 and so on. Notice that for some of 
>> those states (2, 3), there is a current active exception object, and for 
>> some (0, 1) there is not. That means that the value of the exception 
>> object is undefined for some predecessor blocks, so a regular phi node, 
>> won't work, and even storing the exception pointer in a local variable 
>> won't work because the compiler will notice that there are some code 
>> paths where the exception variable never gets set. Now, I happen to know 
>> that the code will never jump to a catch handler when there is no active 
>> throwable, but I am not sure that the compiler knows this.
>>     
>
> IIRC, llvm-gcc duplicates cleanups a lot.  Less than gcc though!
>
> Good luck,
>
> Duncan.
> _______________________________________________
> LLVM Developers mailing list
> LLVMdev at cs.uiuc.edu         http://llvm.cs.uiuc.edu
> http://lists.cs.uiuc.edu/mailman/listinfo/llvmdev
>
>   




More information about the llvm-dev mailing list