From 646073b30f09ba58353698432dccf8a3af0ff895 Mon Sep 17 00:00:00 2001 From: Reid Kleckner Date: Thu, 1 Oct 2015 21:38:24 +0000 Subject: [PATCH] [WinEH] Emit __C_specific_handler tables for the new IR 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 | 2 + lib/CodeGen/AsmPrinter/EHStreamer.h | 8 +- lib/CodeGen/AsmPrinter/WinException.cpp | 245 +++++++++++++++++++----- lib/CodeGen/WinEHPrepare.cpp | 28 ++- lib/Target/X86/X86FrameLowering.cpp | 19 +- test/CodeGen/X86/seh-catchpad.ll | 202 +++++++++++++++++++ 6 files changed, 439 insertions(+), 65 deletions(-) create mode 100644 test/CodeGen/X86/seh-catchpad.ll diff --git a/include/llvm/CodeGen/WinEHFuncInfo.h b/include/llvm/CodeGen/WinEHFuncInfo.h index 79f21687302..5d122094ff8 100644 --- a/include/llvm/CodeGen/WinEHFuncInfo.h +++ b/include/llvm/CodeGen/WinEHFuncInfo.h @@ -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; diff --git a/lib/CodeGen/AsmPrinter/EHStreamer.h b/lib/CodeGen/AsmPrinter/EHStreamer.h index e42e082acbf..c6a0e9d0524 100644 --- a/lib/CodeGen/AsmPrinter/EHStreamer.h +++ b/lib/CodeGen/AsmPrinter/EHStreamer.h @@ -76,10 +76,6 @@ protected: SmallVectorImpl &Actions, SmallVectorImpl &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 &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); }; } diff --git a/lib/CodeGen/AsmPrinter/WinException.cpp b/lib/CodeGen/AsmPrinter/WinException.cpp index f4a279e5fe8..b0c6b7d11e4 100644 --- a/lib/CodeGen/AsmPrinter/WinException.cpp +++ b/lib/CodeGen/AsmPrinter/WinException.cpp @@ -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 +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 &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(); + 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 &PadInfos = MMI->getLandingPads(); SmallVector 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> &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; } } diff --git a/lib/CodeGen/WinEHPrepare.cpp b/lib/CodeGen/WinEHPrepare.cpp index 07734c352c0..3b6187594e9 100644 --- a/lib/CodeGen/WinEHPrepare.cpp +++ b/lib/CodeGen/WinEHPrepare.cpp @@ -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(CPI->getArgOperand(0)->stripPointerCasts()); + const Constant *FilterOrNull = + cast(CPI->getArgOperand(0)->stripPointerCasts()); + const Function *Filter = dyn_cast(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(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'); diff --git a/lib/Target/X86/X86FrameLowering.cpp b/lib/Target/X86/X86FrameLowering.cpp index fc192cd6bfd..c2c9f07ee10 100644 --- a/lib/Target/X86/X86FrameLowering.cpp +++ b/lib/Target/X86/X86FrameLowering.cpp @@ -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 index 00000000000..e69bc018d83 --- /dev/null +++ b/test/CodeGen/X86/seh-catchpad.ll @@ -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 } -- 2.34.1