[llvm-commits] [llvm-gcc-4.2] r102148 - /llvm-gcc-4.2/trunk/gcc/llvm-convert.cpp

Bill Wendling wendling at apple.com
Fri Apr 23 01:02:53 PDT 2010


On Apr 22, 2010, at 11:42 PM, Duncan Sands wrote:

> Hi Bill,
> 
>> A fix for a nasty bug (<rdar://problem/7885482>). Here's what's happening:
>> 
>> Consider this pseudo-ObjC code:
>> 
>>       // locking using some "lock" variable
>>       @try {
>>         // ...
>>       } @catch (...) {
>>         // ...
>>         @throw;
>>       } @finally {
>>         // unlocking using the "lock" variable
>>       }
>> 
>> The "lock" variable has live intervals from the top of the function through the
>> @try block and in the @finally block. On 32-bit x86, it doesn't have a live
>> interval in the @catch block. This is because in 32-bit mode Objective-C uses
>> setjmp/longjmp for exception handling and not the invoke/DWARF method.  The
>> @throw is implemented as an "objc_exception_throw" call marked with
>> NORETURN. The upshot is that if the "lock" variable is placed into a stack slot,
>> there won't be an indication that the "lock" can be used after the
>> "objc_exception_throw" executes. With the invoke/DWARF method, the unwind edge
>> of the invoke points to the @finally block, so the "lock" variable will have a
>> live interval leading to there.
>> 
>> The solution is to have the "objc_exception_throw" behave in a similar manner to
>> the invoke/DWARF method. That is remove the "NORETURN" attribute, allowing it to
>> have an edge from the call to the @finally block.
> 
> I didn't understand your explanation.  Can you please try again using smaller
> words!  Alternatively, add a testcase and I will work it out for myself.
> 
The testcase that we have is long-ish and doesn't really lend itself to an easy understanding of what's going on. What's happening is that there is a stack slot that we're storing a value into at the start of the function, and then accessing it within the @finally block. The control-flow for the @throw instruction is something like this:

  call void @objc_exception_throw(%struct.NSObject* %9) noreturn
  unreachable

The variable we stored into the stack slot at the beginning isn't used within the @catch block. The stack slot coloring code algorithm sees that the live intervals for that stack slot are from the start of the function to the end of the @try block and from the start of the @finally block to its end. It thinks that it's okay to reuse that stack slot inside of the @catch block. However, because of the semantics of the @finally block, the stack slot isn't really available in the @catch, even though there isn't an edge from the end of the @catch to anywhere else in the function.

In short, there's a "false hole" in the live range for a variable that's residing on the stack.

>> +    if (!TARGET_64BIT&&  Callee->getName() == "objc_exception_throw")
>> +      cast<Function>(Callee)->removeFnAttr(Attribute::NoReturn);
> 
> Wouldn't this be better off in the objc front-end?

I tried removing the "noreturn" attribute inside of the objc directory, but it didn't work (it caused a segfault). I think it's safer to do it in llvm-convert.cpp, because we know at that time that we want it removed and we know the semantics of the generated IR won't be violated. You know about the semantics of GCC's IR, but it's a bit of a black box for me. Changing something like an attribute on a tree node is scary to me. :-)

> Also, if I understood your
> description right (which I don't think I did), can't this kind of thing go
> wrong for any code that make use of setjmp/longjmp?

I don't think so; not if we create the CFG correctly it won't. This really is a special case having to do with exception handling being implemented with setjmp/longjmp and how it interacts with LLVM's assumptions about EH.

-bw





More information about the llvm-commits mailing list