Getting exceptions to work?

I've been struggling for several days, trying to get native exceptions to work in my code. I managed to boil down the IR to the simplest possible example that I can think of.

If anyone on this list can tell me what changes I need to make to the following code to get it to work (i.e. return 0 instead of a bus error), it would make my life immensely better.

    ; ModuleID = 'ExceptionTest'
        %Object = type {}
        %UnwindInfo = type { i64, void (i32, %UnwindInfo *)*, i16, i16 }
        %Throwable = type { %Object, %UnwindInfo }

    define i32 @main(i32, i8**) nounwind {
    entry:
        invoke fastcc void @throwSomething() to label %nounwind unwind
    label %catch

    catch:
        ret i32 0

    nounwind:
        ret i32 -1
    }

    define internal fastcc void @throwSomething() noreturn {
    entry:
        %throwable = malloc %Throwable
        call fastcc void @Throwable.construct(%Throwable* %throwable)
        %unwindInfo = getelementptr %Throwable* %throwable, i32 0, i32 1
        %throw = call i32 @_Unwind_RaiseException(%UnwindInfo* %unwindInfo)
        unreachable
    }

    define internal fastcc void @Throwable.construct(%Throwable* %self)
    nounwind {
    entry:
        %exceptionClass = getelementptr %Throwable* %self, i32 0, i32 1,
    i32 0
        store i64 0, i64* %exceptionClass
        %exceptionCleanup = getelementptr %Throwable* %self, i32 0, i32
    1, i32 1
        store void (i32, %UnwindInfo *)* @exceptionCleanupFn, void (i32,
    %UnwindInfo *)** %exceptionCleanup
        %private1 = getelementptr %Throwable* %self, i32 0, i32 1, i32 2
        store i16 0, i16* %private1
        %private2 = getelementptr %Throwable* %self, i32 0, i32 1, i32 3
        store i16 0, i16* %private2
        ret void
    }

    define internal void @exceptionCleanupFn(i32 %reason, %UnwindInfo *
    %exc) {
      ret void
    }

    declare i32 @_Unwind_RaiseException(%UnwindInfo*) noreturn

-- Talin

ExTest.ll (1.33 KB)

It looks like you're missing some required code in main() to catch the
exception. Try comparing with the output of "llvm-gcc -x c++ - -o -
-S -O2 -emit-llvm" for a simple program like the following:

__attribute__((noinline)) int a() { throw 1; } int main() { try { a();
} catch (...) { return 0; } return 1; }

-Eli

Hi Talin,

You're not using the llvm intrinsics for exception handling, so your code won't work. Using _Unwind_RaiseException should be OK, but your main function must at least use llvm.eh.exception, llvm.eh.selector.i32/64 and probably __cxa_begin_catch and __cxa_end_catch.

Nicolas

Talin wrote:

Hi Talin,

I've been struggling for several days, trying to get native exceptions to work in my code. I managed to boil down the IR to the simplest possible example that I can think of.

currently exception handling requires you to specify a personality
function (do this using an selector intrinsic).

Ciao,

Duncan.

Nicolas Geoffray wrote:

Hi Talin,

You're not using the llvm intrinsics for exception handling, so your code won't work. Using _Unwind_RaiseException should be OK, but your main function must at least use llvm.eh.exception, llvm.eh.selector.i32/64 and probably __cxa_begin_catch and __cxa_end_catch.
  

Let me ask a follow-up question then - if the llvm.eh.* intrinsics are required to define the landing pad, then what is the role of the "unwind" target in the invoke instruction? According to the docs, the unwind target defines where execution will jump to when the called function returns with an exception, but clearly that isn't true - the flow of control passes directly to the personality function, bypassing the unwind target completely (as far as I can tell).

By using the llvm.eh* intrinsics, I have managed to get the code to call my custom personality function without crashing. However, what I don't yet understand is how to get the result from the personality function back to the landing pad. For example, I could use the function _Unwind_SetIP to tell it where to jump to, except that I can't take the address of a label in LLVM. I could call _Unwind_SetGR to set an index and then have a switch in the landing pad that uses that index - except that I would have no way to determine which register is safe to set in a platform-independent way. I could even just have the personality function do nothing at all, and have the landing pad do all the work - except that if my personality function doesn't do anything, then the landing pad is skipped entirely, and control resumes at the non-unwind target of the original invoke instruction, as if no exception had been thrown at all.

Hello, Talin

By using the llvm.eh* intrinsics, I have managed to get the code to call
my custom personality function without crashing. However, what I don't
yet understand is how to get the result from the personality function
back to the landing pad.

You might find this useful:
http://www.codesourcery.com/public/cxx-abi/abi-eh.html#cxx-catch

Anton Korobeynikov wrote:

Hello, Talin

By using the llvm.eh* intrinsics, I have managed to get the code to call
my custom personality function without crashing. However, what I don't
yet understand is how to get the result from the personality function
back to the landing pad.
    

You might find this useful:
http://www.codesourcery.com/public/cxx-abi/abi-eh.html#cxx-catch
  

I've been reading that. But what's not clear is how to translate that into LLVM-speak. For example, what value would you pass to _Unwind_SetIP?

-- Talin

Talin wrote:

Nicolas Geoffray wrote:

Hi Talin,

You're not using the llvm intrinsics for exception handling, so your code won't work. Using _Unwind_RaiseException should be OK, but your main function must at least use llvm.eh.exception, llvm.eh.selector.i32/64 and probably __cxa_begin_catch and __cxa_end_catch.
  

Let me ask a follow-up question then - if the llvm.eh.* intrinsics are required to define the landing pad, then what is the role of the "unwind" target in the invoke instruction?

They are not required to define a landing pad. They are currently
required by the code generators. The interpreter works fine without
them for example. The current design is pretty horrible: the dwarf
eh intrinsics were tacked on to the pre-existing invoke-unwind scheme,
and it shows. Anyway, I plan to teach the code generators that if no
personality function was specified then it should use the C personality
function (yes, there is one). This is probably good enough for simple
cases such as running cleanups.

According to the docs, the

unwind target defines where execution will jump to when the called function returns with an exception, but clearly that isn't true - the
flow of control passes directly to the personality function, bypassing the unwind target completely (as far as I can tell).

The language description describes how it is supposed to work. It
simply hasn't been implemented yet in the code generator.
Obviously some code is necessarily executed by the run-time when an
exception is thrown, to unwind the stack etc. You should think of the
personality function as part of the runtime.

By using the llvm.eh* intrinsics, I have managed to get the code to call my custom personality function without crashing. However, what I don't yet understand is how to get the result from the personality function back to the landing pad. For example, I could use the function _Unwind_SetIP to tell it where to jump to, except that I can't take the address of a label in LLVM. I could call _Unwind_SetGR to set an index and then have a switch in the landing pad that uses that index - except that I would have no way to determine which register is safe to set in a platform-independent way. I could even just have the personality function do nothing at all, and have the landing pad do all the work - except that if my personality function doesn't do anything, then the landing pad is skipped entirely, and control resumes at the non-unwind target of the original invoke instruction, as if no exception had been thrown at all.

I would just use the C personality function, __gcc_personality_v0, if
I were you. It should know where to jump to because the code generators
record the invoke unwind target in the dwarf exception handling info in
the object file.

Ciao,

Duncan.

Duncan Sands wrote:

I would just use the C personality function, __gcc_personality_v0, if
I were you. It should know where to jump to because the code generators
record the invoke unwind target in the dwarf exception handling info in
the object file.

I see. OK, I will try that.

I was hoping to be able to test the exception type in the personality function - the language I am working on has its own type system, unrelated to C or C++, so I want to minimize reliance on C++ exception handling libraries. In this case, I guess I can do the exception type checking in the landing pad instead of the personality function.

-- Talin

Talin wrote:

I see. OK, I will try that.

I was hoping to be able to test the exception type in the personality function - the language I am working on has its own type system, unrelated to C or C++, so I want to minimize reliance on C++ exception handling libraries. In this case, I guess I can do the exception type checking in the landing pad instead of the personality function.
  
Yes, that's what VMKit used to do, until it realized dwarf exceptions should never be used for something like Java :slight_smile:

Nicolas

Can you elaborate on that last point a bit? What problems did you run into?

-- Talin

DWARF exceptions (aka zero-cost exceptions) are optimized to be efficient when no exceptions are thrown. They're not efficient at all when exceptions are thrown, so they tend to be a poor match for languages like Java where exceptions are relatively common.

--Owen

Duncan Sands wrote:

I would just use the C personality function, __gcc_personality_v0, if
I were you. It should know where to jump to because the code generators
record the invoke unwind target in the dwarf exception handling info in
the object file.

So I tried what you suggested, and it just gives me a bus error:

    define i32 @main(i32, i8**) nounwind {
    entry:
       call void @print(i8* bitcast ([6 x i8]* @str_begin to i8 *))
       invoke fastcc void @throwSomething() to label %nounwind unwind
    label %catch;

    catch:
      %eh_ptr = call i8* @llvm.eh.exception();
      %eh_select34 = call i32 (i8*, i8*, ...)*
          @llvm.eh.selector.i32 (
              i8* %eh_ptr,
              i8* bitcast (i32 (i32, i32, i64, i8*, %UnwindContext*)*
    @__gcc_personality_v0 to i8*),
              i32 1)

      call void @print(i8* bitcast ([6 x i8]* @str_catch to i8 *))
      ret i32 0

    nounwind:
      call void @print(i8* bitcast ([8 x i8]* @str_nocatch to i8 *))
      ret i32 -1
    }

However, when I use my own personality function instead of __gcc_personality_v0, it doesn't crash, but it doesn't work right either (for reasons we've already discussed - it doesn't know how to redirect the execution to the landing pad):

    @str_persn = internal constant [10 x i8] c"persn %d\0a\00"
    define i32 @personality_func(i32 %version, i32 %action, i64
    %eh_class, i8* %eh_ptr, %UnwindContext* %eh_context) nounwind {
      call void (i8*, ...)* @printf(i8* bitcast ([10 x i8]* @str_persn
    to i8*), i32 %action)

      %phase = icmp eq i32 %action, 1
      br i1 %phase, label %search, label %handler

    search:
      ret i32 6
         handler:
      call void @_Unwind_SetIP(%UnwindContext* %eh_context, i8* %cc0)
      ret i32 7
    }

-- Talin

Owen Anderson wrote:

DWARF exceptions (aka zero-cost exceptions) are optimized to be efficient when no exceptions are thrown. They're not efficient at all when exceptions are thrown, so they tend to be a poor match for languages like Java where exceptions are relatively common.

Exactly. For instance, VMKit completes the exception-intensive jack benchmark (SPEC JVM98) in 60 seconds with Dwarf exceptions, and in 10 seconds with exception checks after each call. On applications with no exceptions, the exception check code does not cost that much (maybe 1 or 2%).

Nicolas

Hi Talin,

So I tried what you suggested, and it just gives me a bus error:

      %eh_select34 = call i32 (i8*, i8*, ...)*
          @llvm.eh.selector.i32 (
              i8* %eh_ptr,
              i8* bitcast (i32 (i32, i32, i64, i8*, %UnwindContext*)*
    @__gcc_personality_v0 to i8*),
              i32 1)

try changing i32 1 to i32 0. Also, feel free to send me something
I can compile, and I will try it here.

Ciao,

Duncan.

Duncan Sands wrote:

Hi Talin,
  

So I tried what you suggested, and it just gives me a bus error:
          %eh_select34 = call i32 (i8*, i8*, ...)*
          @llvm.eh.selector.i32 (
              i8* %eh_ptr,
              i8* bitcast (i32 (i32, i32, i64, i8*, %UnwindContext*)*
    @__gcc_personality_v0 to i8*),
              i32 1)
    
try changing i32 1 to i32 0. Also, feel free to send me something
I can compile, and I will try it here.
  

File attached.

I compile and run with:

  llvm-as -f ExTest.ll && llc -f ExTest.bc && gcc -g -o ExTest ExTest.s && ./ExTest

-- Talin

ExTest.ll (3.2 KB)

Hi Talin, if you change @throwSomething so that it prints a message
if the _Unwind_RaiseException call returns, then you will see that
it does in fact return.

define internal fastcc void @throwSomething() {
entry:
         %throwable = malloc %Throwable
         call fastcc void @Throwable.construct(%Throwable* %throwable)
         %unwindInfo = getelementptr %Throwable* %throwable, i32 0, i32 1
         call void @print(i8* bitcast ([6 x i8]* @str_throw to i8 *))
         %throw = call i32 @_Unwind_RaiseException(%UnwindInfo* %unwindInfo)
         call void @print(i8* bitcast ([6 x i8]* @str_abort to i8 *))
         ret void
}

This is because it unwound the stack without finding any exception
handlers. It doesn't consider your exception handler to be a handler
because it is registered as a "cleanup" which apparently doesn't count.
(To force cleanups to be run in this situation you have to call the gcc
forced unwind routine after calling Unwind_RaiseException).

Of course you would probably like to have your handler be considered a
real handler. Sadly you can't use __gcc_personality_v0 for this: it
only handles cleanups, and if it sees something else (eg: if you specify
a catch-all in the selector) then it tells the unwinder to keep on
unwinding, which is not what you want.

So indeed you either need to write your own personality routine or use
a more powerful personality function like the C++ personality, and use
an eh.selector call to specify a catch-all (use null for this).

In order to get correct invoke semantics, LLVM really needs its own
personality function. The problem is that LLVM doesn't have its own
runtime library like libgcc. This could be overcome by outputting the
LLVM personality into each assembler file that needs it, with weak
linkage so that multiple copies get collapsed to one at link time.

Ciao,

Duncan.

Well that clears up a little bit of the mystery - thanks for doing that research.

I recognize the problem with runtime libraries for LLVM. I am in fact writing a generic runtime library as part of the garbage collector work I am doing - it uses policy-based templates so that things like the threading model, atomic operations and error reporting can all be replaced by the host environment as needed.

That being said, my immediate problem is still getting exceptions to work.

The biggest problem that I foresee with writing my own personality function is dealing with all of the different platforms. Both LLVM and glibc isolate me to some extent from platform idiosyncracies, meaning that I can for the most part code against a platform-agnostic API and not worry about it. However, the innards of glibc are no doubt radically different between platforms, which will bring me a world of pain if I attempt to replace parts of it.

On the other hand, at some point I was going to have to do this anyway, given that I wanted to support compiling under MSVC as well as GCC eventually. I feel that LLVM relies too heavily on functionality provided by glibc, but I recognize that for most of the people working on it, that option makes the most sense and meets their needs.

I don’t actually think that LLVM needs its own standard personality function per se. I think what it needs is a few additional intrinsics that will allow someone like me to write a personality function. An example would be intrinsics that wrap around the various libunwind methods, but which hide the specific platform details like register numbers and such.

How complex do you think a standard personality function would likely be? This is kind of hard for me to guess, since I don’t quite understand the division of labor between the personality function and the landing pad (I get the impression its flexible).

I can think of several ways in which a personality function might work:

  1. The personality function examines the type of the exception object and determines what label to branch to in the function containing the handler. The biggest problem with this is that there’s no way for the personality function to take a reference to a label at the IR level.

  2. The personality function examines the type of the exception object, and returns an integer code telling the landing pad where to branch to. The landing pad does a switch instruction and branches to the correct label. The problem here is that passing values between the personality function and the landing pad cannot be done at the IR level without knowing intimate details about the platform.

  3. The personality function merely returns true or false, indicating an exception was caught or not - it’s the landing pad’s responsibility to examine the exception type and branch accordingly. This type of personality function would be relatively simple (if it worked) and completely generic. At the same time, however, it is potentially the least efficient, since it cannot make use of any of the jump table information generated by the compiler, nor can it determine if the exception was in fact caught - it has to assume that the exception was always caught, and then if the landing pad later determines that there was no catch clause for that particular exception type, then it has to rethrow it. (Note, I have no idea whether or not either of these issues are in any way significant, since I don’t know what kind of code the compiler generates.)

Well, after much labor, I think I have managed to create a personality function that works. But I must say it is frightening how complicated all of this stuff is.

-- Talin

Duncan Sands wrote:

Well, after much labor, I think I have managed to create a personality function that works. But I must say it is frightening how complicated all of this stuff is.

Yup. Having personally suffered quite a bit of pain in this area,
I've come to the conclusion that if you want to use the gcc unwinder
and personality functions then you have to carefully copy what gcc
does: it's fragile stuff and any deviation will cause breakage.
That makes mapping LLVM's exception handling model onto libgcc in an
efficient way unpleasantly tricky.

Ciao,

Duncan.