[WinEH] Emit __C_specific_handler tables for the new IR
authorReid Kleckner <rnk@google.com>
Thu, 1 Oct 2015 21:38:24 +0000 (21:38 +0000)
committerReid Kleckner <rnk@google.com>
Thu, 1 Oct 2015 21:38:24 +0000 (21:38 +0000)
We emit denormalized tables, where every range of invokes in the same
state gets a complete list of EH action entries. This is significantly
simpler than trying to infer the correct nested scoping structure from
the MI. Fortunately, for SEH, the nesting structure is really just a
size optimization.

With this, some basic __try / __except examples work.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@249078 91177308-0d34-0410-b5e6-96231b3b80d8

include/llvm/CodeGen/WinEHFuncInfo.h
lib/CodeGen/AsmPrinter/EHStreamer.h
lib/CodeGen/AsmPrinter/WinException.cpp
lib/CodeGen/WinEHPrepare.cpp
lib/Target/X86/X86FrameLowering.cpp
test/CodeGen/X86/seh-catchpad.ll [new file with mode: 0644]

index 79f216873024bc8526b1a571b106324cb81912c6..5d122094ff8b0a477de98d1f40b243e57e29a1f2 100644 (file)
@@ -131,6 +131,8 @@ struct SEHUnwindMapEntry {
   /// this state. This indexes into SEHUnwindMap.
   int ToState = -1;
 
+  bool IsFinally = false;
+
   /// Holds the filter expression function.
   const Function *Filter = nullptr;
 
index e42e082acbf96fa811ea20f8119a5eae7bbd3034..c6a0e9d0524c4d361b38cd591065fd24ad033bad 100644 (file)
@@ -76,10 +76,6 @@ protected:
                                SmallVectorImpl<ActionEntry> &Actions,
                                SmallVectorImpl<unsigned> &FirstActions);
 
-  /// Return `true' if this is a call to a function marked `nounwind'. Return
-  /// `false' otherwise.
-  bool callToNoUnwindFunction(const MachineInstr *MI);
-
   void computePadMap(const SmallVectorImpl<const LandingPadInfo *> &LandingPads,
                      RangeMapType &PadMap);
 
@@ -131,6 +127,10 @@ public:
   void setSymbolSize(const MCSymbol *Sym, uint64_t Size) override {}
   void beginInstruction(const MachineInstr *MI) override {}
   void endInstruction() override {}
+
+  /// Return `true' if this is a call to a function marked `nounwind'. Return
+  /// `false' otherwise.
+  static bool callToNoUnwindFunction(const MachineInstr *MI);
 };
 }
 
index f4a279e5fe8651b5e7c08159f3bc76a5e0ccff76..b0c6b7d11e4dfffb515b29c47afc534d1b03576c 100644 (file)
@@ -121,6 +121,10 @@ void WinException::endFunction(const MachineFunction *MF) {
 
   endFunclet();
 
+  // endFunclet will emit the necessary .xdata tables for x64 SEH.
+  if (Per == EHPersonality::MSVC_Win64SEH && MMI->hasEHFunclets())
+    return;
+
   if (shouldEmitPersonality || shouldEmitLSDA) {
     Asm->OutStreamer->PushSection();
 
@@ -237,14 +241,19 @@ void WinException::endFunclet() {
     // Emit an UNWIND_INFO struct describing the prologue.
     Asm->OutStreamer->EmitWinEHHandlerData();
 
-    // If this is a C++ catch funclet (or the parent function),
-    // emit a reference to the LSDA for the parent function.
     if (Per == EHPersonality::MSVC_CXX && shouldEmitPersonality &&
         !CurrentFuncletEntry->isCleanupFuncletEntry()) {
+      // If this is a C++ catch funclet (or the parent function),
+      // emit a reference to the LSDA for the parent function.
       StringRef FuncLinkageName = GlobalValue::getRealLinkageName(F->getName());
       MCSymbol *FuncInfoXData = Asm->OutContext.getOrCreateSymbol(
           Twine("$cppxdata$", FuncLinkageName));
       Asm->OutStreamer->EmitValue(create32bitRef(FuncInfoXData), 4);
+    } else if (Per == EHPersonality::MSVC_Win64SEH && MMI->hasEHFunclets() &&
+               !CurrentFuncletEntry->isEHFuncletEntry()) {
+      // If this is the parent function in Win64 SEH, emit the LSDA immediately
+      // following .seh_handlerdata.
+      emitCSpecificHandlerTable(Asm->MF);
     }
 
     // Switch back to the previous section now that we are done writing to
@@ -283,6 +292,96 @@ const MCExpr *WinException::getLabelPlusOne(MCSymbol *Label) {
                                  Asm->OutContext);
 }
 
+/// Information describing an invoke range.
+struct InvokeRange {
+  MCSymbol *BeginLabel = nullptr;
+  MCSymbol *EndLabel = nullptr;
+  int State = -1;
+
+  /// If we saw a potentially throwing call between this range and the last
+  /// range.
+  bool SawPotentiallyThrowing = false;
+};
+
+/// Iterator over the begin/end label pairs of invokes within a basic block.
+class InvokeLabelIterator {
+public:
+  InvokeLabelIterator(WinEHFuncInfo &EHInfo,
+                      MachineBasicBlock::const_iterator MBBI,
+                      MachineBasicBlock::const_iterator MBBIEnd)
+      : EHInfo(EHInfo), MBBI(MBBI), MBBIEnd(MBBIEnd) {
+    scan();
+  }
+
+  // Iterator methods.
+  bool operator==(const InvokeLabelIterator &o) const { return MBBI == o.MBBI; }
+  bool operator!=(const InvokeLabelIterator &o) const { return MBBI != o.MBBI; }
+  InvokeRange &operator*() { return CurRange; }
+  InvokeRange *operator->() { return &CurRange; }
+  InvokeLabelIterator &operator++() { return scan(); }
+
+private:
+  // Scan forward to find the next invoke range, or hit the end iterator.
+  InvokeLabelIterator &scan();
+
+  WinEHFuncInfo &EHInfo;
+  MachineBasicBlock::const_iterator MBBI;
+  MachineBasicBlock::const_iterator MBBIEnd;
+  InvokeRange CurRange;
+};
+
+/// Invoke label range iteration logic. Increment MBBI until we find the next
+/// EH_LABEL pair, and then update MBBI to point after the end label.
+InvokeLabelIterator &InvokeLabelIterator::scan() {
+  // Reset our state.
+  CurRange = InvokeRange{};
+
+  for (const MachineInstr &MI : make_range(MBBI, MBBIEnd)) {
+    // Remember if we had to cross a potentially throwing call instruction that
+    // must unwind to caller.
+    if (MI.isCall()) {
+      CurRange.SawPotentiallyThrowing |=
+          !EHStreamer::callToNoUnwindFunction(&MI);
+      continue;
+    }
+    // Find the next EH_LABEL instruction.
+    if (!MI.isEHLabel())
+      continue;
+
+    // If this is a begin label, break out with the state and end label.
+    // Otherwise this is probably a CFI EH_LABEL that we should continue past.
+    MCSymbol *Label = MI.getOperand(0).getMCSymbol();
+    auto StateAndEnd = EHInfo.InvokeToStateMap.find(Label);
+    if (StateAndEnd == EHInfo.InvokeToStateMap.end())
+      continue;
+    MBBI = MachineBasicBlock::const_iterator(&MI);
+    CurRange.BeginLabel = Label;
+    CurRange.EndLabel = StateAndEnd->second.second;
+    CurRange.State = StateAndEnd->second.first;
+    break;
+  }
+
+  // If we didn't find a begin label, we are done, return the end iterator.
+  if (!CurRange.BeginLabel) {
+    MBBI = MBBIEnd;
+    return *this;
+  }
+
+  // If this is a begin label, update MBBI to point past the end label.
+  for (; MBBI != MBBIEnd; ++MBBI)
+    if (MBBI->isEHLabel() &&
+        MBBI->getOperand(0).getMCSymbol() == CurRange.EndLabel)
+      break;
+  return *this;
+}
+
+/// Utility for making a range for all the invoke ranges.
+static iterator_range<InvokeLabelIterator>
+invoke_ranges(WinEHFuncInfo &EHInfo, const MachineBasicBlock &MBB) {
+  return make_range(InvokeLabelIterator(EHInfo, MBB.begin(), MBB.end()),
+                    InvokeLabelIterator(EHInfo, MBB.end(), MBB.end()));
+}
+
 /// Emit the language-specific data that __C_specific_handler expects.  This
 /// handler lives in the x64 Microsoft C runtime and allows catching or cleaning
 /// up after faults with __try, __except, and __finally.  The typeinfo values
@@ -312,11 +411,86 @@ const MCExpr *WinException::getLabelPlusOne(MCSymbol *Label) {
 ///     } Entries[NumEntries];
 ///   };
 void WinException::emitCSpecificHandlerTable(const MachineFunction *MF) {
-  const std::vector<LandingPadInfo> &PadInfos = MMI->getLandingPads();
+  auto &OS = *Asm->OutStreamer;
+  MCContext &Ctx = Asm->OutContext;
 
   WinEHFuncInfo &FuncInfo = MMI->getWinEHFuncInfo(MF->getFunction());
-  if (!FuncInfo.SEHUnwindMap.empty())
-    report_fatal_error("x64 SEH tables not yet implemented");
+  if (!FuncInfo.SEHUnwindMap.empty()) {
+    // Remember what state we were in the last time we found a begin try label.
+    // This allows us to coalesce many nearby invokes with the same state into
+    // one entry.
+    int LastEHState = -1;
+    MCSymbol *LastBeginLabel = nullptr;
+    MCSymbol *LastEndLabel = nullptr;
+
+    // Use the assembler to compute the number of table entries through label
+    // difference and division.
+    MCSymbol *TableBegin = Ctx.createTempSymbol("lsda_begin");
+    MCSymbol *TableEnd = Ctx.createTempSymbol("lsda_end");
+    const MCExpr *LabelDiff =
+        MCBinaryExpr::createSub(MCSymbolRefExpr::create(TableEnd, Ctx),
+                                MCSymbolRefExpr::create(TableBegin, Ctx), Ctx);
+    const MCExpr *EntrySize = MCConstantExpr::create(16, Ctx);
+    const MCExpr *EntryCount =
+        MCBinaryExpr::createDiv(LabelDiff, EntrySize, Ctx);
+    OS.EmitValue(EntryCount, 4);
+
+    OS.EmitLabel(TableBegin);
+
+    // Iterate over all the invoke try ranges. Unlike MSVC, LLVM currently only
+    // models exceptions from invokes. LLVM also allows arbitrary reordering of
+    // the code, so our tables end up looking a bit different. Rather than
+    // trying to match MSVC's tables exactly, we emit a denormalized table.  For
+    // each range of invokes in the same state, we emit table entries for all
+    // the actions that would be taken in that state. This means our tables are
+    // slightly bigger, which is OK.
+    for (const auto &MBB : *MF) {
+      for (InvokeRange &I : invoke_ranges(FuncInfo, MBB)) {
+        // If this invoke is in the same state as the last invoke and there were
+        // no non-throwing calls between it, extend the range to include both
+        // and continue.
+        if (!I.SawPotentiallyThrowing && I.State == LastEHState) {
+          LastEndLabel = I.EndLabel;
+          continue;
+        }
+
+        // If this invoke ends a previous one, emit all the actions for this
+        // state.
+        if (LastEHState != -1) {
+          assert(LastBeginLabel && LastEndLabel);
+          for (int State = LastEHState; State != -1;) {
+            SEHUnwindMapEntry &UME = FuncInfo.SEHUnwindMap[State];
+            const MCExpr *FilterOrFinally;
+            const MCExpr *ExceptOrNull;
+            auto *Handler = UME.Handler.get<MachineBasicBlock *>();
+            if (UME.IsFinally) {
+              FilterOrFinally = create32bitRef(Handler->getSymbol());
+              ExceptOrNull = MCConstantExpr::create(0, Ctx);
+            } else {
+              // For an except, the filter can be 1 (catch-all) or a function
+              // label.
+              FilterOrFinally = UME.Filter ? create32bitRef(UME.Filter)
+                                           : MCConstantExpr::create(1, Ctx);
+              ExceptOrNull = create32bitRef(Handler->getSymbol());
+            }
+
+            OS.EmitValue(getLabelPlusOne(LastBeginLabel), 4);
+            OS.EmitValue(getLabelPlusOne(LastEndLabel), 4);
+            OS.EmitValue(FilterOrFinally, 4);
+            OS.EmitValue(ExceptOrNull, 4);
+
+            State = UME.ToState;
+          }
+        }
+
+        LastBeginLabel = I.BeginLabel;
+        LastEndLabel = I.EndLabel;
+        LastEHState = I.State;
+      }
+    }
+    OS.EmitLabel(TableEnd);
+    return;
+  }
 
   // Simplifying assumptions for first implementation:
   // - Cleanups are not implemented.
@@ -324,6 +498,7 @@ void WinException::emitCSpecificHandlerTable(const MachineFunction *MF) {
 
   // The Itanium LSDA table sorts similar landing pads together to simplify the
   // actions table, but we don't need that.
+  const std::vector<LandingPadInfo> &PadInfos = MMI->getLandingPads();
   SmallVector<const LandingPadInfo *, 64> LandingPads;
   LandingPads.reserve(PadInfos.size());
   for (const auto &LP : PadInfos)
@@ -346,7 +521,7 @@ void WinException::emitCSpecificHandlerTable(const MachineFunction *MF) {
       continue; // Ignore gaps.
     NumEntries += CSE.LPad->SEHHandlers.size();
   }
-  Asm->OutStreamer->EmitIntValue(NumEntries, 4);
+  OS.EmitIntValue(NumEntries, 4);
 
   // If there are no actions, we don't need to iterate again.
   if (NumEntries == 0)
@@ -377,25 +552,25 @@ void WinException::emitCSpecificHandlerTable(const MachineFunction *MF) {
 
     // Emit an entry for each action.
     for (SEHHandler Handler : LPad->SEHHandlers) {
-      Asm->OutStreamer->EmitValue(Begin, 4);
-      Asm->OutStreamer->EmitValue(End, 4);
+      OS.EmitValue(Begin, 4);
+      OS.EmitValue(End, 4);
 
       // Emit the filter or finally function pointer, if present. Otherwise,
       // emit '1' to indicate a catch-all.
       const Function *F = Handler.FilterOrFinally;
       if (F)
-        Asm->OutStreamer->EmitValue(create32bitRef(Asm->getSymbol(F)), 4);
+        OS.EmitValue(create32bitRef(Asm->getSymbol(F)), 4);
       else
-        Asm->OutStreamer->EmitIntValue(1, 4);
+        OS.EmitIntValue(1, 4);
 
       // Emit the recovery address, if present. Otherwise, this must be a
       // finally.
       const BlockAddress *BA = Handler.RecoverBA;
       if (BA)
-        Asm->OutStreamer->EmitValue(
+        OS.EmitValue(
             create32bitRef(Asm->GetBlockAddressSymbol(BA)), 4);
       else
-        Asm->OutStreamer->EmitIntValue(0, 4);
+        OS.EmitIntValue(0, 4);
     }
   }
 }
@@ -583,10 +758,6 @@ void WinException::emitCXXFrameHandler3Table(const MachineFunction *MF) {
 void WinException::computeIP2StateTable(
     const MachineFunction *MF, WinEHFuncInfo &FuncInfo,
     SmallVectorImpl<std::pair<const MCExpr *, int>> &IPToStateTable) {
-  // Whether there is a potentially throwing instruction (currently this means
-  // an ordinary call) between the end of the previous try-range and now.
-  bool SawPotentiallyThrowing = true;
-
   // Remember what state we were in the last time we found a begin try label.
   // This allows us to coalesce many nearby invokes with the same state into one
   // entry.
@@ -602,49 +773,23 @@ void WinException::computeIP2StateTable(
   for (const auto &MBB : *MF) {
     // FIXME: Do we need to emit entries for funclet base states?
 
-    for (const auto &MI : MBB) {
-      // Find all the EH_LABEL instructions, tracking if we've crossed a
-      // potentially throwing call since the last label.
-      if (!MI.isEHLabel()) {
-        if (MI.isCall())
-          SawPotentiallyThrowing |= !callToNoUnwindFunction(&MI);
-        continue;
-      }
-
-      // If this was an end label, return SawPotentiallyThrowing to the start
-      // state and keep going. Otherwise, we will consider the call between the
-      // begin/end labels to be a potentially throwing call and generate extra
-      // table entries.
-      MCSymbol *Label = MI.getOperand(0).getMCSymbol();
-      if (Label == LastEndLabel)
-        SawPotentiallyThrowing = false;
-
-      // Check if this was a begin label. Otherwise, it must be an end label or
-      // some random label, and we should continue.
-      auto StateAndEnd = FuncInfo.InvokeToStateMap.find(Label);
-      if (StateAndEnd == FuncInfo.InvokeToStateMap.end())
-        continue;
-
-      // Extract the state and end label.
-      int State;
-      MCSymbol *EndLabel;
-      std::tie(State, EndLabel) = StateAndEnd->second;
-
+    for (InvokeRange &I : invoke_ranges(FuncInfo, MBB)) {
+      assert(I.BeginLabel && I.EndLabel);
       // If there was a potentially throwing call between this begin label and
       // the last end label, we need an extra base state entry to indicate that
       // those calls unwind directly to the caller.
-      if (SawPotentiallyThrowing && LastEHState != -1) {
+      if (I.SawPotentiallyThrowing && LastEHState != -1) {
         IPToStateTable.push_back(
             std::make_pair(getLabelPlusOne(LastEndLabel), -1));
-        SawPotentiallyThrowing = false;
         LastEHState = -1;
       }
 
       // Emit an entry indicating that PCs after 'Label' have this EH state.
-      if (State != LastEHState)
-        IPToStateTable.push_back(std::make_pair(create32bitRef(Label), State));
-      LastEHState = State;
-      LastEndLabel = EndLabel;
+      if (I.State != LastEHState)
+        IPToStateTable.push_back(
+            std::make_pair(create32bitRef(I.BeginLabel), I.State));
+      LastEHState = I.State;
+      LastEndLabel = I.EndLabel;
     }
   }
 
index 07734c352c0622a62fe2f2460874961b48eff09e..3b6187594e95a930f1497718ff004a24e2a9c05a 100644 (file)
@@ -2724,16 +2724,28 @@ static void calculateExplicitCXXStateNumbers(WinEHFuncInfo &FuncInfo,
   }
 }
 
-static int addSEHHandler(WinEHFuncInfo &FuncInfo, int ParentState,
-                         const Function *Filter, const BasicBlock *Handler) {
+static int addSEHExcept(WinEHFuncInfo &FuncInfo, int ParentState,
+                        const Function *Filter, const BasicBlock *Handler) {
   SEHUnwindMapEntry Entry;
   Entry.ToState = ParentState;
+  Entry.IsFinally = false;
   Entry.Filter = Filter;
   Entry.Handler = Handler;
   FuncInfo.SEHUnwindMap.push_back(Entry);
   return FuncInfo.SEHUnwindMap.size() - 1;
 }
 
+static int addSEHFinally(WinEHFuncInfo &FuncInfo, int ParentState,
+                         const BasicBlock *Handler) {
+  SEHUnwindMapEntry Entry;
+  Entry.ToState = ParentState;
+  Entry.IsFinally = true;
+  Entry.Filter = nullptr;
+  Entry.Handler = Handler;
+  FuncInfo.SEHUnwindMap.push_back(Entry);
+  return FuncInfo.SEHUnwindMap.size() - 1;
+}
+
 static void calculateExplicitSEHStateNumbers(WinEHFuncInfo &FuncInfo,
                                              const BasicBlock &BB,
                                              int ParentState) {
@@ -2753,10 +2765,13 @@ static void calculateExplicitSEHStateNumbers(WinEHFuncInfo &FuncInfo,
            "SEH doesn't have multiple handlers per __try");
     const CatchPadInst *CPI = Handlers.front();
     const BasicBlock *CatchPadBB = CPI->getParent();
-    const Function *Filter =
-        cast<Function>(CPI->getArgOperand(0)->stripPointerCasts());
+    const Constant *FilterOrNull =
+        cast<Constant>(CPI->getArgOperand(0)->stripPointerCasts());
+    const Function *Filter = dyn_cast<Function>(FilterOrNull);
+    assert((Filter || FilterOrNull->isNullValue()) &&
+           "unexpected filter value");
     int TryState =
-        addSEHHandler(FuncInfo, ParentState, Filter, CPI->getNormalDest());
+        addSEHExcept(FuncInfo, ParentState, Filter, CPI->getNormalDest());
 
     // Everything in the __try block uses TryState as its parent state.
     FuncInfo.EHPadStateMap[CPI] = TryState;
@@ -2775,8 +2790,7 @@ static void calculateExplicitSEHStateNumbers(WinEHFuncInfo &FuncInfo,
       if ((PredBlock = getEHPadFromPredecessor(PredBlock)))
         calculateExplicitSEHStateNumbers(FuncInfo, *PredBlock, ParentState);
   } else if (isa<CleanupPadInst>(FirstNonPHI)) {
-    int CleanupState =
-        addSEHHandler(FuncInfo, ParentState, /*Filter=*/nullptr, &BB);
+    int CleanupState = addSEHFinally(FuncInfo, ParentState, &BB);
     FuncInfo.EHPadStateMap[FirstNonPHI] = CleanupState;
     DEBUG(dbgs() << "Assigning state #" << CleanupState << " to BB "
                  << BB.getName() << '\n');
index fc192cd6bfd2304dce4bd82ce369c143569644dd..c2c9f07ee10fe3d2f612f0daf1095d6e57a6419e 100644 (file)
@@ -1489,10 +1489,21 @@ bool X86FrameLowering::restoreCalleeSavedRegisters(MachineBasicBlock &MBB,
   if (CSI.empty())
     return false;
 
-  // Don't restore CSRs in 32-bit EH funclets. Matches
-  // spillCalleeSavedRegisters.
-  if (isFuncletReturnInstr(MI) && STI.is32Bit() && STI.isOSWindows())
-    return true;
+  if (isFuncletReturnInstr(MI) && STI.isOSWindows()) {
+    // Don't restore CSRs in 32-bit EH funclets. Matches
+    // spillCalleeSavedRegisters.
+    if (STI.is32Bit())
+      return true;
+    // Don't restore CSRs before an SEH catchret. SEH except blocks do not form
+    // funclets. emitEpilogue transforms these to normal jumps.
+    if (MI->getOpcode() == X86::CATCHRET) {
+      const Function *Func = MBB.getParent()->getFunction();
+      bool IsSEH = isAsynchronousEHPersonality(
+          classifyEHPersonality(Func->getPersonalityFn()));
+      if (IsSEH)
+        return true;
+    }
+  }
 
   DebugLoc DL = MBB.findDebugLoc(MI);
 
diff --git a/test/CodeGen/X86/seh-catchpad.ll b/test/CodeGen/X86/seh-catchpad.ll
new file mode 100644 (file)
index 0000000..e69bc01
--- /dev/null
@@ -0,0 +1,202 @@
+; RUN: llc < %s | FileCheck %s
+
+; Based on the source:
+; extern "C" int puts(const char *);
+; extern "C" int printf(const char *, ...);
+; extern "C" int do_div(int a, int b) { return a / b; }
+; extern "C" int filt();
+; int main() {
+;   __try {
+;     __try {
+;       do_div(1, 0);
+;     } __except (1) {
+;       __try {
+;         do_div(1, 0);
+;       } __finally {
+;         puts("finally");
+;       }
+;     }
+;   } __except (filt()) {
+;     puts("caught");
+;   }
+;   return 0;
+; }
+
+; ModuleID = 't.cpp'
+target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-pc-windows-msvc"
+
+$"\01??_C@_07MKBLAIAL@finally?$AA@" = comdat any
+
+$"\01??_C@_06IBDBCMGJ@caught?$AA@" = comdat any
+
+@"\01??_C@_07MKBLAIAL@finally?$AA@" = linkonce_odr unnamed_addr constant [8 x i8] c"finally\00", comdat, align 1
+@"\01??_C@_06IBDBCMGJ@caught?$AA@" = linkonce_odr unnamed_addr constant [7 x i8] c"caught\00", comdat, align 1
+
+; Function Attrs: nounwind readnone
+define i32 @do_div(i32 %a, i32 %b) #0 {
+entry:
+  %div = sdiv i32 %a, %b
+  ret i32 %div
+}
+
+define i32 @main() #1 personality i8* bitcast (i32 (...)* @__C_specific_handler to i8*) {
+entry:
+  %call = invoke i32 @do_div(i32 1, i32 0) #4
+          to label %__try.cont.12 unwind label %catch.dispatch
+
+catch.dispatch:                                   ; preds = %entry
+  %0 = catchpad [i8* null]
+          to label %__except unwind label %catchendblock
+
+__except:                                         ; preds = %catch.dispatch
+  catchret %0 to label %__except.2
+
+__except.2:                                       ; preds = %__except
+  %call4 = invoke i32 @do_div(i32 1, i32 0) #4
+          to label %invoke.cont.3 unwind label %ehcleanup
+
+invoke.cont.3:                                    ; preds = %__except.2
+  invoke fastcc void @"\01?fin$0@0@main@@"() #4
+          to label %__try.cont.12 unwind label %catch.dispatch.7
+
+catchendblock:                                    ; preds = %catch.dispatch
+  catchendpad unwind label %catch.dispatch.7
+
+ehcleanup:                                        ; preds = %__except.2
+  %1 = cleanuppad []
+  invoke fastcc void @"\01?fin$0@0@main@@"() #4
+          to label %invoke.cont.6 unwind label %ehcleanup.end
+
+invoke.cont.6:                                    ; preds = %ehcleanup
+  cleanupret %1 unwind label %catch.dispatch.7
+
+catch.dispatch.7:                                 ; preds = %invoke.cont.3, %invoke.cont.6, %ehcleanup.end, %catchendblock
+  %2 = catchpad [i8* bitcast (i32 (i8*, i8*)* @"\01?filt$0@0@main@@" to i8*)]
+          to label %__except.ret unwind label %catchendblock.8
+
+__except.ret:                                     ; preds = %catch.dispatch.7
+  catchret %2 to label %__except.9
+
+__except.9:                                       ; preds = %__except.ret
+  %call11 = tail call i32 @puts(i8* nonnull getelementptr inbounds ([7 x i8], [7 x i8]* @"\01??_C@_06IBDBCMGJ@caught?$AA@", i64 0, i64 0))
+  br label %__try.cont.12
+
+__try.cont.12:                                    ; preds = %invoke.cont.3, %entry, %__except.9
+  ret i32 0
+
+catchendblock.8:                                  ; preds = %catch.dispatch.7
+  catchendpad unwind to caller
+
+ehcleanup.end:                                    ; preds = %ehcleanup
+  cleanupendpad %1 unwind label %catch.dispatch.7
+}
+
+; CHECK: main:                                   # @main
+; CHECK: .seh_proc main
+; CHECK:         .seh_handler __C_specific_handler, @unwind, @except
+; CHECK:         pushq   %rbp
+; CHECK:         .seh_pushreg 5
+; CHECK:         subq    $48, %rsp
+; CHECK:         .seh_stackalloc 48
+; CHECK:         leaq    48(%rsp), %rbp
+; CHECK:         .seh_setframe 5, 48
+; CHECK:         .seh_endprologue
+; CHECK: .Ltmp0:
+; CHECK:         movl    $1, %ecx
+; CHECK:         xorl    %edx, %edx
+; CHECK:         callq   do_div
+; CHECK: .Ltmp1:
+; CHECK: .LBB1_[[epilogue:[0-9]+]]:                                # %__try.cont.12
+; CHECK:         xorl    %eax, %eax
+; CHECK:         addq    $48, %rsp
+; CHECK:         popq    %rbp
+; CHECK:         retq
+; CHECK: .LBB1_[[except1bb:[0-9]+]]:                                # %__except
+; CHECK: .Ltmp2:
+; CHECK:         movl    $1, %ecx
+; CHECK:         xorl    %edx, %edx
+; CHECK:         callq   do_div
+; CHECK: .Ltmp3:
+; CHECK:         callq   "?fin$0@0@main@@"
+; CHECK:         jmp     .LBB1_[[epilogue]]
+; CHECK: .LBB1_[[except2bb:[0-9]+]]:                                # %__except.ret
+; CHECK:         leaq    "??_C@_06IBDBCMGJ@caught?$AA@"(%rip), %rcx
+; CHECK:         callq   puts
+; CHECK:         jmp     .LBB1_[[epilogue]]
+
+; CHECK:         .seh_handlerdata
+; CHECK-NEXT:         .long   (.Ltmp14-.Ltmp13)/16
+; CHECK-NEXT: .Ltmp13:
+; CHECK-NEXT:         .long   .Ltmp0@IMGREL
+; CHECK-NEXT:         .long   .Ltmp1@IMGREL+1
+; CHECK-NEXT:         .long   1
+; CHECK-NEXT:         .long   .LBB1_[[except1bb]]@IMGREL
+; CHECK-NEXT:         .long   .Ltmp0@IMGREL
+; CHECK-NEXT:         .long   .Ltmp1@IMGREL+1
+; CHECK-NEXT:         .long   "?filt$0@0@main@@"@IMGREL
+; CHECK-NEXT:         .long   .LBB1_[[except2bb]]@IMGREL
+; CHECK-NEXT:         .long   .Ltmp2@IMGREL
+; CHECK-NEXT:         .long   .Ltmp3@IMGREL+1
+; CHECK-NEXT:         .long   .LBB1_[[finbb:[0-9]+]]@IMGREL
+; CHECK-NEXT:         .long   0
+; CHECK-NEXT:         .long   .Ltmp2@IMGREL
+; CHECK-NEXT:         .long   .Ltmp3@IMGREL+1
+; CHECK-NEXT:         .long   "?filt$0@0@main@@"@IMGREL
+; CHECK-NEXT:         .long   .LBB1_6@IMGREL
+; CHECK-NEXT: .Ltmp14:
+
+; CHECK:         .text
+; CHECK:         .seh_endproc
+
+; CHECK: "?dtor$4@?0?main@4HA":
+; CHECK: .seh_proc "?dtor$4@?0?main@4HA"
+; CHECK:         .seh_handler __C_specific_handler, @unwind, @except
+; CHECK: .LBB1_[[finbb]]:                                # %ehcleanup
+; CHECK:         movq    %rdx, 16(%rsp)
+; CHECK:         pushq   %rbp
+; CHECK:         .seh_pushreg 5
+; CHECK:         subq    $32, %rsp
+; CHECK:         .seh_stackalloc 32
+; CHECK:         leaq    48(%rdx), %rbp
+; CHECK:         .seh_endprologue
+; CHECK:         callq   "?fin$0@0@main@@"
+; CHECK:         nop
+; CHECK:         addq    $32, %rsp
+; CHECK:         popq    %rbp
+; CHECK:         retq
+; CHECK:         .seh_handlerdata
+; CHECK:         .seh_endproc
+
+define internal i32 @"\01?filt$0@0@main@@"(i8* nocapture readnone %exception_pointers, i8* nocapture readnone %frame_pointer) #1 {
+entry:
+  %call = tail call i32 @filt()
+  ret i32 %call
+}
+
+; CHECK: "?filt$0@0@main@@":                     # @"\01?filt$0@0@main@@"
+; CHECK: .seh_proc "?filt$0@0@main@@"
+; CHECK:         .seh_endprologue
+; CHECK:         rex64 jmp       filt  # TAILCALL
+; CHECK:         .seh_handlerdata
+
+declare i32 @filt() #1
+
+declare i32 @__C_specific_handler(...)
+
+; Function Attrs: noinline nounwind
+define internal fastcc void @"\01?fin$0@0@main@@"() #2 {
+entry:
+  %call = tail call i32 @puts(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @"\01??_C@_07MKBLAIAL@finally?$AA@", i64 0, i64 0)) #5
+  ret void
+}
+
+; Function Attrs: nounwind
+declare i32 @puts(i8* nocapture readonly) #3
+
+attributes #0 = { nounwind readnone "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-features"="+sse,+sse2" "unsafe-fp-math"="false" "use-soft-float"="false" }
+attributes #1 = { "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-features"="+sse,+sse2" "unsafe-fp-math"="false" "use-soft-float"="false" }
+attributes #2 = { noinline nounwind "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-features"="+sse,+sse2" "unsafe-fp-math"="false" "use-soft-float"="false" }
+attributes #3 = { nounwind "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-features"="+sse,+sse2" "unsafe-fp-math"="false" "use-soft-float"="false" }
+attributes #4 = { noinline }
+attributes #5 = { nounwind }