Reid Kleckner
2014-Nov-19 02:43 UTC
[LLVMdev] RFC: How to represent SEH (__try / __except) in LLVM IR
On Tue, Nov 18, 2014 at 5:52 PM, Kaylor, Andrew <andrew.kaylor at intel.com> wrote:> > For DWARF EH and SjLj, the backend is responsible for handling most of > the EH work. It seems like it would be a more consistent design for SEH to > do the same. > > > > Looking beyond SEH to C++ exception handling for a moment, it seems to me > that clang may be handling more than it should there. For instance, calls > like “__cxa_allocate_exception” and “__cxa_throw_exception” are baked into > the clang IR output, which seems to assume that the backend is going to be > using libc++abi for its implementation. Yet it has enough awareness that > this won’t always be true that it coughs up an ErrorUnsupported failure for > “isWindowsMSVCEnvironment” targets when asked to emit code for “try” or > “throw”. > > > > Should this be generalized with intrinsics? >We should just teach Clang to emit calls to the appropriate runtime functions. This isn't needed for SEH because you don't "throw", you just crash.> Also, I’m starting to dig into the outlining implementation and there are > some things there that worry me. I haven’t compared any existing code that > might be doing similar things, so maybe these issues will become clear as I > get further into it, but it seemed worth bringing it up now to smooth the > progress. I’m trying to put together a general algorithm that starts at > the landing pad instruction and groups the subsequent instructions as > cleanup code or parts of catch handlers. This is easy enough to do as a > human reading the code, but the way that I’m doing so seems to rely fairly > heavily on the names of symbols and labels. >Look at lib/Transforms/Utils/CloneFunction.cpp. Most of that code should be factored appropriately and reused. It uses a ValueMapping that we should be able to apply to the landing pad instruction to map the ehselector.slot to a constant, and propagating that through.> For instance, following the landingpad instruction I expect to find an > extract and store of “exn.slot” and “ehselector.slot” then everything > between that and wherever the catch dispatch begins must be (I think) > cleanup code. The catch handlers I’m identifying as a sequence that starts > with a load of “exn.slot” and a call to __cxa_begin_catch and continues > until it reaches a call to __cxa_end_catch. >I think we'll have to intrinsic-ify __cxa_end_catch when targeting *-windows-msvc to get this right. If we don't, exception rethrows will probably not work. We don't really need an equivalent of __cxa_begin_catch because there's no thread-local EH state to update, it's already managed by the caller of the catch handler.> The calls to begin/end catch are pretty convenient bookends, but > identifying the catch dispatch code and pairing catch handlers with the > clauses they represent seems to depend on recognizing the pattern of > loading the ehselector, getting a typeid then comparing and branching. I > suppose that will work, but it feels a bit brittle. Then there’s the > cleanup code, which I’m not yet convinced has a consistent location > relative to the catch dispatching and I fear may be moved around by various > optimizations before the outlining and will potentially be partially shared > with cleanup for other landing pads. >We either have to pattern match the selector == typeid pattern in the EH preparation pass, or come up with a new representation. I'm hesitant to add a new EH representation that only MSVC compatible EH uses, because it will probably trip up existing optimizations. I was hoping that something like the pruning logic in "llvm::CloneAndPruneFunctionInto" would allow us to prune the selector comparison branches reliably. -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20141118/ec8e42c8/attachment.html>
Kaylor, Andrew
2014-Nov-24 20:12 UTC
[LLVMdev] RFC: How to represent SEH (__try / __except) in LLVM IR
Hi Reid, I've been working on the outlining code and have a prototype that produces what I want for a simple case. Now I'm thinking about the heuristics for recognizing the various logical pieces for C++ exception handling code and removing them once they’ve been cloned. I've been working from various comments you've made earlier in this thread, and I'd like to run something by you to make sure we're on the same page. Starting from a C++ function that looks like this: void do_some_thing(int &i) { Outer outer; try { Middle middle; if (i == 1) { do_thing_one(); } else { Inner inner; do_thing_two(); } } catch (int en) { i = -1; } } I'll have IR that looks more or less like this: ; Function Attrs: uwtable define void @_Z13do_some_thingRi(i32* dereferenceable(4) %i) #0 { entry: %i.addr = alloca i32*, align 8 %outer = alloca %class.Outer, align 1 %middle = alloca %class.Middle, align 1 %exn.slot = alloca i8* %ehselector.slot = alloca i32 %inner = alloca %class.Inner, align 1 %en = alloca i32, align 4 store i32* %i, i32** %i.addr, align 8 call void @_ZN5OuterC1Ev(%class.Outer* %outer) invoke void @_ZN6MiddleC1Ev(%class.Middle* %middle) to label %invoke.cont unwind label %lpad invoke.cont: ; preds = %entry %0 = load i32** %i.addr, align 8 %1 = load i32* %0, align 4 %cmp = icmp eq i32 %1, 1 br i1 %cmp, label %if.then, label %if.else if.then: ; preds = %invoke.cont invoke void @_Z12do_thing_onev() to label %invoke.cont2 unwind label %lpad1 invoke.cont2: ; preds = %if.then br label %if.end ; From 'entry' invoke of Middle constructor ; outer needs post-catch cleanup lpad: ; preds = %if.end, %entry %2 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) cleanup catch i8* bitcast (i8** @_ZTIi to i8*) %3 = extractvalue { i8*, i32 } %2, 0 store i8* %3, i8** %exn.slot %4 = extractvalue { i8*, i32 } %2, 1 store i32 %4, i32* %ehselector.slot ; No pre-catch cleanup for this landingpad br label %catch.dispatch ; From 'if.then' invoke of do_thing_one() ; Or from 'if.else' invoke of Inner constructor ; Or from 'invoke.cont5 invoke of Inner destructor ; middle needs pre-catch cleanup ; outer needs post-catch cleanup lpad1: ; preds = %invoke.cont5, %if.else, %if.then %5 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) cleanup catch i8* bitcast (i8** @_ZTIi to i8*) %6 = extractvalue { i8*, i32 } %5, 0 store i8* %6, i8** %exn.slot %7 = extractvalue { i8*, i32 } %5, 1 store i32 %7, i32* %ehselector.slot ; Branch to shared label to do pre-catch cleanup br label %ehcleanup if.else: ; preds = %invoke.cont invoke void @_ZN5InnerC1Ev(%class.Inner* %inner) to label %invoke.cont3 unwind label %lpad1 invoke.cont3: ; preds = %if.else invoke void @_Z12do_thing_twov() to label %invoke.cont5 unwind label %lpad4 invoke.cont5: ; preds = %invoke.cont3 invoke void @_ZN5InnerD1Ev(%class.Inner* %inner) to label %invoke.cont6 unwind label %lpad1 invoke.cont6: ; preds = %invoke.cont5 br label %if.end ; From 'invoke.cont3' invoke of do_something_two() ; middle and inner need pre-catch cleanup ; outer needs post-catch cleanup lpad4: ; preds = %invoke.cont3 %8 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) cleanup catch i8* bitcast (i8** @_ZTIi to i8*) %9 = extractvalue { i8*, i32 } %8, 0 store i8* %9, i8** %exn.slot %10 = extractvalue { i8*, i32 } %8, 1 store i32 %10, i32* %ehselector.slot ; Pre-catch cleanup begins here, but will continue at ehcleanup invoke void @_ZN5InnerD1Ev(%class.Inner* %inner) to label %invoke.cont7 unwind label %terminate.lpad invoke.cont7: ; preds = %lpad4 br label %ehcleanup if.end: ; preds = %invoke.cont6, %invoke.cont2 invoke void @_ZN6MiddleD1Ev(%class.Middle* %middle) to label %invoke.cont8 unwind label %lpad invoke.cont8: ; preds = %if.end br label %try.cont ; Pre-catch cleanup for lpad1 ; Continuation of pre-catch cleanup for lpad4 ehcleanup: ; preds = %invoke.cont7, %lpad1 invoke void @_ZN6MiddleD1Ev(%class.Middle* %middle) to label %invoke.cont9 unwind label %terminate.lpad invoke.cont9: ; preds = %ehcleanup br label %catch.dispatch ; Catch dispatch for lpad, lpad1 and lpad4 catch.dispatch: ; preds = %invoke.cont9, %lpad %sel = load i32* %ehselector.slot %11 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*)) #4 %matches = icmp eq i32 %sel, %11 br i1 %matches, label %catch, label %ehcleanup10 catch: ; preds = %catch.dispatch %exn = load i8** %exn.slot %12 = call i8* @__cxa_begin_catch(i8* %exn) #4 %13 = bitcast i8* %12 to i32* %14 = load i32* %13, align 4 store i32 %14, i32* %en, align 4 %15 = load i32** %i.addr, align 8 store i32 -1, i32* %15, align 4 call void @__cxa_end_catch() #4 br label %try.cont try.cont: ; preds = %catch, %invoke.cont8 call void @_ZN5OuterD1Ev(%class.Outer* %outer) ret void ; Post catch cleanup for lpad, lpad1 ehcleanup10: ; preds = %catch.dispatch invoke void @_ZN5OuterD1Ev(%class.Outer* %outer) to label %invoke.cont11 unwind label %terminate.lpad invoke.cont11: ; preds = %ehcleanup10 br label %eh.resume eh.resume: ; preds = %invoke.cont11 %exn12 = load i8** %exn.slot %sel13 = load i32* %ehselector.slot %lpad.val = insertvalue { i8*, i32 } undef, i8* %exn12, 0 %lpad.val14 = insertvalue { i8*, i32 } %lpad.val, i32 %sel13, 1 resume { i8*, i32 } %lpad.val14 terminate.lpad: ; preds = %ehcleanup10, %ehcleanup, %lpad4 %16 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) catch i8* null %17 = extractvalue { i8*, i32 } %16, 0 call void @__clang_call_terminate(i8* %17) #5 unreachable } If I've understood your intentions correctly, we'll have an outlining pass that transforms the above IR to this: %struct.do_some_thing.captureblock = type { %class.Outer, %class.Middle, %class.Inner, %i32* } ; Uncaught exception cleanup for lpad, lpad1 and lpad4 define void @do_some_thing_cleanup0(i8* %eh_ptrs, i8* %rbp) #0 { entry: %capture.block = call @llvm.eh.get_capture_block(@_Z13do_some_thingRi , %rbp) %outer = getelementptr inbounds %struct.do_some_this.captureblock* %capture.block, i32 0, i32 0 invoke void @_ZN5OuterD1Ev(%class.Outer* %outer) to label %invoke.cont unwind label %terminate.lpad invoke.cont: ret void terminate.lpad: ; preds = %ehcleanup10, %ehcleanup, %lpad4 %0 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) catch i8* null %1 = extractvalue { i8*, i32 } %0, 0 call void @__clang_call_terminate(i8* %1) #5 unreachable } ; Catch handler for _ZTIi define i8* @do_some_thing_catch0(i8* %eh_ptrs, i8* %rbp) #0 { entry: %capture.block = call @llvm.eh.get_capture_block(@_Z13do_some_thingRi , %rbp) %i.addr = getelementptr inbounds %struct.do_some_this.captureblock* %capture.block, i32 0, i32 4 %1 = load i32** %i.addr, align 8 store i32 -1, i32* %1, align 4 ret i8* blockaddress(@_Z13do_some_thingRi, %try.cont) } ; Outlined pre-catch cleanup handler for lpad1 define void @do_some_thing_cleanup1(i8* %eh_ptrs, i8* %rbp) #0 { entry: %capture.block = call @llvm.eh.get_capture_block(@_Z13do_some_thingRi, %rbp) ; Outlined from 'ehcleanup' %middle = getelementptr inbounds %struct.do_some_this.captureblock* %capture.block, i32 0, i32 1 invoke void @_ZN6MiddleD1Ev(%class.Middle* %middle) to label %invoke.cont unwind label %terminate.lpad invoke.cont: ret void terminate.lpad: ; preds = %ehcleanup10, %ehcleanup, %lpad4 %0 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) catch i8* null %1 = extractvalue { i8*, i32 } %0, 0 call void @__clang_call_terminate(i8* %1) #5 unreachable } ; Outlined pre-catch cleanup handler for 'lpad4' define void @do_some_thing_cleanup2(i8* %eh_ptrs, i8* %rbp) #0 { entry: %capture.block = call @llvm.eh.get_capture_block(@_Z13do_some_thingRi , %rbp) ; Outlined from 'lpad4' %inner = getelementptr inbounds %struct.do_some_this.captureblock* %capture.block, i32 0, i32 2 invoke void @_ZN5InnerD1Ev(%class.Inner* %inner) to label %invoke.cont unwind label %terminate.lpad invoke.cont: ; preds = %entry ; Outlined from 'ehcleanup' %middle = getelementptr inbounds %struct.do_some_this.captureblock* %capture.block, i32 0, i32 1 invoke void @_ZN6MiddleD1Ev(%class.Middle* %middle) to label %invoke.cont1 unwind label %terminate.lpad invoke.cont1: ret void terminate.lpad: ; preds = %ehcleanup10, %ehcleanup, %lpad4 %0 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) catch i8* null %1 = extractvalue { i8*, i32 } %0, 0 call void @__clang_call_terminate(i8* %1) #5 unreachable } ; Function Attrs: uwtable define void @_Z13do_some_thingRi(i32* dereferenceable(4) %i) #0 { entry: %capture.block = alloca %struct.do_some_thing.capture.block, align 1 %i_addr = getelementptr inbounds %struct.do_some_thing_capture_block* %capture_block, i32 0, i32 3 store i32* %i, i32** %i_addr, align 8 llvm.eh.set_capture_block %eh.cont.label = alloca i8* %en = alloca i32, align 4 store i32* %i, i32** %i.addr, align 8 %outer = getelementptr inbounds %struct.do_some_thing.capture.block* %capture.block, i32 0, i32 0 call void @_ZN5OuterC1Ev(%class.Outer* %outer) %middle = getelementptr inbounds %struct.do_some_thing.capture.block* %capture.block, i32 0, i32 1 invoke void @_ZN6MiddleC1Ev(%class.Middle* %middle) to label %invoke.cont unwind label %lpad invoke.cont: ; preds = %entry %0 = load i32** %i.addr, align 8 %1 = load i32* %0, align 4 %cmp = icmp eq i32 %1, 1 br i1 %cmp, label %if.then, label %if.else if.then: ; preds = %invoke.cont invoke void @_Z12do_thing_onev() to label %invoke.cont2 unwind label %lpad1 invoke.cont2: ; preds = %if.then br label %if.end ; From 'entry' invoke of Middle constructor ; outer needs post-catch cleanup lpad: ; preds = %if.end, %entry %2 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) cleanup catch i8* bitcast (i8** @_ZTIi to i8*) %eh.cont.label = call i8* (...)* @llvm.eh.outlined_handlers( i8* @_ZTIi, i8* (i8*, i8*)* @do_some_thing_catch0, void (i8*, i8*)* @do_some_thing_cleanup0) indirectbr i8* %eh.cont.label ; From 'if.then' invoke of do_thing_one() ; Or from 'if.else' invoke of Inner constructor ; Or from 'invoke.cont5 invoke of Inner destructor ; middle needs pre-catch cleanup ; outer needs post-catch cleanup lpad1: ; preds = %invoke.cont5, %if.else, %if.then %5 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) cleanup catch i8* bitcast (i8** @_ZTIi to i8*) %eh.cont.label = call i8* (...)* @llvm.eh.outlined_handlers( void (i8*, i8*)* @do_some_thing_cleanup1, i8* @_ZTIi, i8* (i8*, i8*)* @do_some_thing_catch0, void (i8*, i8*)* @do_some_thing_cleanup0) indirectbr i8* %eh.cont.label if.else: ; preds = %invoke.cont %inner = getelementptr inbounds %struct.do_some_thing.capture.block* %capture.block, i32 0, i32 2 invoke void @_ZN5InnerC1Ev(%class.Inner* %inner) to label %invoke.cont3 unwind label %lpad1 invoke.cont3: ; preds = %if.else invoke void @_Z12do_thing_twov() to label %invoke.cont5 unwind label %lpad4 invoke.cont5: ; preds = %invoke.cont3 invoke void @_ZN5InnerD1Ev(%class.Inner* %inner) to label %invoke.cont6 unwind label %lpad1 invoke.cont6: ; preds = %invoke.cont5 br label %if.end ; From 'invoke.cont3' invoke of do_something_two() ; middle and inner need pre-catch cleanup ; outer needs post-catch cleanup lpad4: ; preds = %invoke.cont3 %8 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) cleanup catch i8* bitcast (i8** @_ZTIi to i8*) %eh.cont.label = call i8* (...)* @llvm.eh.outlined_handlers( void (i8*, i8*)* @do_some_thing_cleanup2, i8* @_ZTIi, i8* (i8*, i8*)* @do_some_thing_catch0, void (i8*, i8*)* @do_some_thing_cleanup0) indirectbr i8* %eh.cont.label if.end: ; preds = %invoke.cont6, %invoke.cont2 invoke void @_ZN6MiddleD1Ev(%class.Middle* %middle) to label %invoke.cont8 unwind label %lpad invoke.cont8: ; preds = %if.end br label %try.cont try.cont: ; preds = %catch, %invoke.cont8 call void @_ZN5OuterD1Ev(%class.Outer* %outer) ret void } Does that look about like what you’d expect? I just have a few questions. I'm pretty much just guessing at how you intended the llvm.eh.set_capture_block intrinsic to work. It wasn't clear to me if I just needed to set it where the structure was created or if it would need to be set anywhere an exception might be thrown. The answer is probably related to my next question. In the above example I created a single capture block for the entire function. That works reasonably well for a simple case like this and corresponds to the co-location of the allocas in the original IR, but for functions with more complex structures and multiple try blocks it could get ugly. Do you have ideas for how to handle that? For C++ exception handling, we need cleanup code that executes before the catch handlers and cleanup code that excutes in the case on uncaught exceptions. I think both of these need to be outlined for the MSVC environment. Do you think we need a stub handler to be inserted in cases where no actual cleanup is performed? I didn't do that in the mock-up above, but it seems like it would simplify things. Basically, I'm imagining a final pattern that looks like this: lpad: %eh_vals = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) cleanup catch i8* @typeid1 catch i8* @typeid2 ... %label = call i8* (...)* @llvm.eh.outlined_handlers( void (i8*, i8*)* @<pre-catch cleanup function>, i8* @typeid1, i8* (i8*, i8*)* @<typeid1 catch function>, i8* @typeid2, i8* (i8*, i8*)* @<typeid2 catch function>, ... void (i8*, i8*)* @<uncaught exception cleanup function>) indirectbr i8* %label Finally, how do you see this meshing with SEH? As I understand it, both the exception handlers and the cleanup code in that case execute in the original function context and only the filter handlers need to be outlined. I suppose the outlining pass can look at the personality function and change its behavior accordingly. Is that what you were thinking? -Andy -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20141124/41d384d1/attachment.html>
Reid Kleckner
2014-Nov-24 21:37 UTC
[LLVMdev] RFC: How to represent SEH (__try / __except) in LLVM IR
On Mon, Nov 24, 2014 at 12:12 PM, Kaylor, Andrew <andrew.kaylor at intel.com> wrote:> Hi Reid, > > > > I've been working on the outlining code and have a prototype that produces > what I want for a simple case. > > > > Now I'm thinking about the heuristics for recognizing the various logical > pieces for C++ exception handling code and removing them once they’ve been > cloned. I've been working from various comments you've made earlier in > this thread, and I'd like to run something by you to make sure we're on the > same page. > > > > Starting from a C++ function that looks like this: >...> I'll have IR that looks more or less like this: >...> If I've understood your intentions correctly, we'll have an outlining pass > that transforms the above IR to this: >...> Does that look about like what you’d expect? >Yep! That's basically what I had in mind, but I still have concerns with this model listed below. We should also think about how to call std::terminate when cleanup dtors throw. The current representation for Itanium is inefficient. As a strawman, I propose making @__clang_call_terminate an intrinsic: ... invoke void @dtor(i8* %this) to label %cont unwind label %terminate.lpad cont: ret void terminate.lpad: landingpad ... catch i8* null call void @llvm.eh.terminate() unreachable This would be good for Itanium EH, as we can actually completely elide table entries for landing pads that just catch-all and terminate.> I just have a few questions. > > > > I'm pretty much just guessing at how you intended the > llvm.eh.set_capture_block intrinsic to work. It wasn't clear to me if I > just needed to set it where the structure was created or if it would need > to be set anywhere an exception might be thrown. The answer is probably > related to my next question. >I was imagining it would be called once in the entry block. Chandler expressed strong concerns about this design, however, as @llvm.eh.get_capture_block adds an ordering constraint on CodeGen. Once you add this intrinsic, we *have* to do frame layout of @_Z13do_some_thingRi *before* we can emit code for all the callers of @llvm.eh.get_capture_block. Today, this is easy, because module order defines emission order, but in the great glorious future, codegen will hopefully be parallelized, and then we've inflicted this horrible constraint on the innocent. His suggestion to break the ordering dependence was to lock down the frame offset of the capture block to always be some fixed offset known by the target (ie ebp - 4 on x86, if we like that). In the above example I created a single capture block for the entire> function. That works reasonably well for a simple case like this and > corresponds to the co-location of the allocas in the original IR, but for > functions with more complex structures and multiple try blocks it could get > ugly. Do you have ideas for how to handle that? >Not really, it would just get ugly. All allocas used from landing pad code would get mushed into one allocation. =/> For C++ exception handling, we need cleanup code that executes before the > catch handlers and cleanup code that excutes in the case on uncaught > exceptions. I think both of these need to be outlined for the MSVC > environment. Do you think we need a stub handler to be inserted in cases > where no actual cleanup is performed? >I think it's actually harder than that, once you consider nested trys: void f() { try { Outer outer; try { Inner inner; g(); } catch (int) { // ~Inner gets run first } } catch (float) { // ~Inner gets run first // ~Outer gets run next } // uncaught exception? Run ~Inner then ~Outer. } It's easy to hit this case after inlining as well. We'd have to generalize @llvm.eh.outlined_handlers more to handle this case. However, if we generalize further it starts to perfectly replicate the landing pad structure, with cleanup, catch, and then we'd want to think about how to represent filter. Termination on exception spec violation seems to be unimplemented in MSVC, so we'd need our own personality function to implement filters, but it'd be good to support them in the IR. We also have to decide how much code duplication of cleanups we're willing to tolerate, and whether we want to try to annotate the beginning and end of cleanups like ~Inner and ~Outer.> I didn't do that in the mock-up above, but it seems like it would simplify > things. Basically, I'm imagining a final pattern that looks like this: > > > > lpad: > > %eh_vals = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* > @__CxxFrameHandler3 to i8*) > > cleanup > > catch i8* @typeid1 > > catch i8* @typeid2 > > ... > > %label = call i8* (...)* @llvm.eh.outlined_handlers( > > void (i8*, i8*)* @<pre-catch cleanup function>, > > i8* @typeid1, i8* (i8*, i8*)* @<typeid1 catch function>, > > i8* @typeid2, i8* (i8*, i8*)* @<typeid2 catch function>, > > ... > > void (i8*, i8*)* @<uncaught exception cleanup function>) > > indirectbr i8* %label > > > > > > Finally, how do you see this meshing with SEH? As I understand it, both > the exception handlers and the cleanup code in that case execute in the > original function context and only the filter handlers need to be > outlined. I suppose the outlining pass can look at the personality > function and change its behavior accordingly. Is that what you were > thinking? >Pretty much. The outlining pass would behave differently based on the personality function. SEH cleanups (__finally blocks) actually do need to get outlined as well as filters, but catches (__except blocks) do not need to be outlined. That's the main difference. I think it reflects the fact that you can rethrow a C++ exception, but you can't faithfully "rethrow" a trap caught by SEH. -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20141124/063d750a/attachment.html>