This is an archive of the discontinued LLVM Phabricator instance.

Use WinEHPrepare to outline SEH finally blocks
ClosedPublic

Authored by rnk on Mar 16 2015, 4:54 PM.

Details

Summary

No outlining is necessary for SEH catch blocks. Use the blockaddr of the
handler in place of the usual outlined function.

Diff Detail

Repository
rL LLVM

Event Timeline

rnk updated this revision to Diff 22065.Mar 16 2015, 4:54 PM
rnk retitled this revision from to Use WinEHPrepare to outline SEH finally blocks.
rnk updated this object.
rnk added reviewers: majnemer, andrew.w.kaylor.
rnk added a subscriber: Unknown Object (MLST).
lib/CodeGen/WinEHPrepare.cpp
94 ↗(On Diff #22065)

We should probably add comments to say which of these fields (I guess all of them currently) are reset with each Function the pass is run on.

465 ↗(On Diff #22065)

What exactly does this do? I've seen some cases recently where cleanup outlining is choking because of PHINodes that are referring to the return value of the landingpad instruction. This is too late to handle that (which just needs a few more VMap entries before outlining) but it raises the question in my mind of general handling of PHINodes which use values that are being erased from the original landing pads here.

I think that all of the code in question will be pruned as part of what we're doing here (it was resume blocks using either the landingpad value or values extracted from it). I'm just raising the issue to see if you can think of anything else that might have a similar issue.

474 ↗(On Diff #22065)

This should be 1 according to your recent documentation.

482 ↗(On Diff #22065)

This should be 0.

728 ↗(On Diff #22065)

In the case of "__except(EXCEPTION_EXECUTE_HANDLER)" I'm seeing the landing pad branch unconditionally to the catch handler.

rnk added a comment.Mar 17 2015, 4:59 PM

I'm going to take a shot at using StartBB instead of using HandlerBB like I did in this patch.

lib/CodeGen/WinEHPrepare.cpp
94 ↗(On Diff #22065)

Yeah, all of em. :)

465 ↗(On Diff #22065)

I was trying to handle except_phi test case:

define i32 @except_phi() {
entry:
  invoke void @might_crash()
          to label %return unwind label %lpad

lpad:
  %ehvals = landingpad { i8*, i32 } personality i32 (...)* @__C_specific_handler
          catch i32 ()* @filt
  %sel = extractvalue { i8*, i32 } %ehvals, 1
  %filt_sel = tail call i32 @llvm.eh.typeid.for(i8* bitcast (i32 ()* @filt to i8*))
  %matches = icmp eq i32 %sel, %filt_sel
  br i1 %matches, label %return, label %eh.resume

return:
  %r = phi i32 [0, %entry], [1, %lpad]
  ret i32 %r

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

I needed to update the %r phi node. Maybe what I should do instead is split the landingpad block directly, because we might have this kind of code:

define i32 @except_phi(i32 %a) {
entry:
  invoke void @might_crash()
          to label %return unwind label %lpad

lpad:
  %ehvals = landingpad { i8*, i32 } personality i32 (...)* @__C_specific_handler
          catch i32 ()* @filt
  %sel = extractvalue { i8*, i32 } %ehvals, 1
  %a1 = add i32 %a, 1 ; live SSA value will become unreachable.
  %filt_sel = tail call i32 @llvm.eh.typeid.for(i8* bitcast (i32 ()* @filt to i8*))
  %matches = icmp eq i32 %sel, %filt_sel
  br i1 %matches, label %return, label %eh.resume

return:
  %r = phi i32 [%a, %entry], [%a1, %lpad]
  ret i32 %r

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

The pass as is will probably crash. I'm OK with that for now, honestly.

474 ↗(On Diff #22065)

I'd rather make that a separate change.

728 ↗(On Diff #22065)

Right, any catch-all block will ignore the selector and unconditionally branch into the recovery. I haven't handled that yet. I think a better approach might be to keep StartBB and simply discard all EH related instructions. This would also fix the phi case.

This revision was automatically updated to reflect the committed changes.
rnk added a comment.Mar 18 2015, 2:27 PM

I added support for catch-all, but I didn't end up handling the case where the catch BB contains interesting code. My reasoning was that EH preparation should be identifying that as a cleanup, and we'll need to do something to handle it for C++.

I see just one problem with this.

llvm/trunk/lib/CodeGen/WinEHPrepare.cpp
1194

This is wrong for the C++ EH case, which can have cleanup code to be executed after a catch-all handler. Setting BB to NULL will prevent use from finding the cleanup in that scenario.

rnk added inline comments.Mar 19 2015, 2:51 PM
llvm/trunk/lib/CodeGen/WinEHPrepare.cpp
1194

Can you elaborate? Is this a cleanup inside the catch, or outside the catch? I don't think it's possible to have exceptional cleanups after a catchall:

void f() {
  HasDtor o;
  try { g(); }
  catch (...) { g(); }
}

Actually, I think I was wrong about the clean-up after catch-all. I thought I had a case that would do it, but it doesn't and I think I've convinced myself that it can't happen.