[PATCH] CodeGen support for x86_64 SEH catch handlers in LLVM

Reid Kleckner rnk at google.com
Tue Nov 18 15:36:23 PST 2014


>>! In D6300#6, @andrew.w.kaylor wrote:
> I've made some specific comments on things that I found confusing.
> 
> Maybe this doesn't need to be entirely settled now, but it isn't clear to me what you imagine the IR to look like before outlining and how the transition from that to the outlined filter functions will happen.  In particular, could you describe what would happen if the filter condition made reference to local variables in the function?
> 
> For instance, suppose I wanted a filter in the safe_div example that looked like this:
> 
> __except(GetExceptionCode() == EXCEPTION_INT_DIVIDE_BY_ZERO && *n == 0)
> 
> what would that look like?

I should post this to the RFC, but I expect the frontend to emit the filter expression code inline in the landingpad, conditional under typeid dispatch. The frontend should create stub filter functions that are filled in by the backend. The backend would trace out basic blocks starting at the landingpad assuming a constant selector value. Tracing would stop at `@llvm.eh.seh.filter`. Any function-ending terminator such as `ret`, `resume`, or `unreachable` would become `unreachable` in the outlined filter function. In the parent function, the oulined code would be deleted.

This has a weird interaction with `__finally`, because we can't outline the cleanup code into the filter function. I think this can be surmounted by changing clang to emit filter expressions before cleanups. This is actually correct under the two-stage unwinding rules given that all filter functions are called until one returns true, all cleanups are called until the final frame is reached, and then control is transferred to the recovering frame.

Here's example IR that I wrote manually:

  define i32 @safe_div_filt0(i8* %eh_ptrs, i8* %rbp) {
    unreachable ; Stub function, filled in by SEHPrepare
  }
  define i32 @safe_div_filt1(i8* %eh_ptrs, i8* %rbp) {
    unreachable ; Stub function, filled in by SEHPrepare
  }
  declare void @cleanup()

  define i32 @safe_div(i32* %n, i32* %d) {
  entry:
    %r = alloca i32, align 4
    invoke void @try_body(i32* %r, i32* %n, i32* %d)
            to label %__try.cont unwind label %lpad

  lpad:
    %vals = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__C_specific_handler to i8*)
            cleanup
            catch i8* bitcast (i32 (i8*, i8*)* @safe_div_filt0 to i8*)
            catch i8* bitcast (i32 (i8*, i8*)* @safe_div_filt1 to i8*)
    %eh_ptrs = extractvalue { i8*, i32 } %vals, 0
    %sel = extractvalue { i8*, i32 } %vals, 1
    %filt0_val = call i32 @llvm.eh.typeid.for(i8* bitcast (i32 (i8*, i8*)* @safe_div_filt0 to i8*))
    %is_filt0 = icmp eq i32 %sel, %filt0_val
    br i1 %is_filt0, label %filter0, label %eh.dispatch1

  eh.dispatch1:
    %filt1_val = call i32 @llvm.eh.typeid.for(i8* bitcast (i32 (i8*, i8*)* @safe_div_filt1 to i8*))
    %is_filt1 = icmp eq i32 %sel, %filt1_val
    br i1 %is_filt1, label %filter1, label %__finally

  filter0:  ; SEHPrepare traces out this basic block, after folding away preceding dispatch.
    %eh_ptrs_c.0 = bitcast i8* %eh_ptrs to i32**
    %eh_rec.0 = load i32** %eh_ptrs_c.0
    %eh_code.0 = load i32* %eh_rec.0
    ; EXCEPTION_ACCESS_VIOLATION = 0xC0000005
    %cmp.0 = icmp eq i32 %eh_code.0, 3221225477
    %filt0.res = zext i1 %cmp.1 to i32
    ; FILTER OUTLINING ENDS HERE
    call void @llvm.eh.seh.filter(i32 %filt0.res)
    br label %__finally

  filter1:
    %eh_ptrs_c.1 = bitcast i8* %eh_ptrs to i32**
    %eh_rec.1 = load i32** %eh_ptrs_c.1
    %eh_code.1 = load i32* %eh_rec.1
    ; EXCEPTION_INT_DIVIDE_BY_ZERO = 0xC0000094
    %cmp.1 = icmp eq i32 %eh_code.1, 3221225620
    %filt1.res = zext i1 %cmp.1 to i32
    ; FILTER OUTLINING ENDS HERE
    call void @llvm.eh.seh.filter(i32 %filt0.res)
    br label %__finally

  __finally:
    call void @cleanup()
    ; Redo eh typeid dispatch.
    br i1 %is_filt0, label %handler0, label %eh.dispatch2

  eh.dispatch2:
    br i1 %is_filt1, label %handler1, label %eh.resume


  handler0:
    call void @puts(i8* getelementptr ([27 x i8]* @str1, i32 0, i32 0))
    store i32 -1, i32* %r, align 4
    br label %__try.cont

  handler1:
    call void @puts(i8* getelementptr ([29 x i8]* @str2, i32 0, i32 0))
    store i32 -2, i32* %r, align 4
    br label %__try.cont

  eh.resume:
    resume { i8*, i32 } %vals

  __try.cont:
    %safe_ret = load i32* %r, align 4
    ret i32 %safe_ret
  }

http://reviews.llvm.org/D6300






More information about the llvm-commits mailing list