where xdata unwind information is used. It is typically an 8 byte chunk of
memory treated as two 32-bit integers.
-``llvm.eh.parentframe``
------------------------
-
-.. code-block:: llvm
-
- void @llvm.eh.parentframe(i8*)
-
-This intrinsic designates the provided static alloca as the object which holds
-the address of the parent frame.
-This object is used by Windows native exception handling on non-x86 platforms
-where xdata unwind information is used.
SJLJ Intrinsics
---------------
struct WinEHHandlerType {
int Adjectives;
- GlobalValue *TypeDescriptor;
+ GlobalVariable *TypeDescriptor;
+ int CatchObjIdx;
+ int CatchObjOffset;
Function *Handler;
};
int TryLow;
int TryHigh;
int CatchHigh;
- SmallVector<WinEHHandlerType, 4> HandlerArray;
+ SmallVector<WinEHHandlerType, 1> HandlerArray;
};
struct WinEHFuncInfo {
// for WinEH.
def int_eh_unwindhelp : Intrinsic<[], [llvm_ptr_ty], []>;
-// Designates the provided static alloca as the object which holds the address
-// of the parent frame. Required for WinEH.
-def int_eh_parentframe : Intrinsic<[], [llvm_ptrptr_ty], []>;
-
// __builtin_unwind_init is an undocumented GCC intrinsic that causes all
// callee-saved registers to be saved and restored (regardless of whether they
// are used) in the calling function. It is used by libgcc_eh.
MCSymbol *getOrCreateSectionSymbol(const MCSectionELF &Section);
MCSymbol *getOrCreateFrameAllocSymbol(StringRef FuncName, unsigned Idx);
+ MCSymbol *getOrCreateParentFrameOffsetSymbol(StringRef FuncName);
/// Get the symbol for \p Name, or null.
MCSymbol *LookupSymbol(const Twine &Name) const;
shouldEmitLSDA = shouldEmitPersonality &&
LSDAEncoding != dwarf::DW_EH_PE_omit;
+
+ // If this was an outlined handler, we need to define the label corresponding
+ // to the offset of the parent frame relative to the stack pointer after the
+ // prologue.
+ const Function *F = MF->getFunction();
+ const Function *ParentF = MMI->getWinEHParent(F);
+ if (F != ParentF) {
+ WinEHFuncInfo &FuncInfo = MMI->getWinEHFuncInfo(ParentF);
+ auto I = FuncInfo.CatchHandlerParentFrameObjOffset.find(F);
+ if (I != FuncInfo.CatchHandlerParentFrameObjOffset.end()) {
+ MCSymbol *HandlerTypeParentFrameOffset =
+ Asm->OutContext.getOrCreateParentFrameOffsetSymbol(
+ GlobalValue::getRealLinkageName(F->getName()));
+
+ // Emit a symbol assignment.
+ Asm->OutStreamer.EmitAssignment(
+ HandlerTypeParentFrameOffset,
+ MCConstantExpr::Create(I->second, Asm->OutContext));
+ }
+ }
+
if (!shouldEmitPersonality && !shouldEmitMoves)
return;
const Function *F = MF->getFunction();
const Function *ParentF = MMI->getWinEHParent(F);
auto &OS = Asm->OutStreamer;
+ WinEHFuncInfo &FuncInfo = MMI->getWinEHFuncInfo(ParentF);
StringRef ParentLinkageName =
GlobalValue::getRealLinkageName(ParentF->getName());
// an ordinary call) between the end of the previous try-range and now.
bool SawPotentiallyThrowing = false;
- WinEHFuncInfo &FuncInfo = MMI->getWinEHFuncInfo(ParentF);
-
int LastEHState = -2;
// The parent function and the catch handlers contribute to the 'ip2state'
// };
OS.EmitLabel(HandlerMapXData);
for (const WinEHHandlerType &HT : TBME.HandlerArray) {
+ MCSymbol *ParentFrameOffset =
+ Asm->OutContext.getOrCreateParentFrameOffsetSymbol(
+ GlobalValue::getRealLinkageName(HT.Handler->getName()));
+ const MCSymbolRefExpr *ParentFrameOffsetRef = MCSymbolRefExpr::Create(
+ ParentFrameOffset, MCSymbolRefExpr::VK_None, Asm->OutContext);
+
OS.EmitIntValue(HT.Adjectives, 4); // Adjectives
OS.EmitValue(createImageRel32(HT.TypeDescriptor), 4); // Type
- OS.EmitIntValue(0, 4); // CatchObjOffset
+ OS.EmitIntValue(HT.CatchObjOffset, 4); // CatchObjOffset
OS.EmitValue(createImageRel32(HT.Handler), 4); // Handler
- OS.EmitIntValue(0, 4); // ParentFrameOffset
+ OS.EmitValue(ParentFrameOffsetRef, 4); // ParentFrameOffset
}
}
}
int NextState;
SmallVector<ActionHandler *, 4> HandlerStack;
+ SmallPtrSet<const Function *, 4> VisitedHandlers;
int currentEHNumber() const {
return HandlerStack.empty() ? -1 : HandlerStack.back()->getEHState();
void parseEHActions(const IntrinsicInst *II,
SmallVectorImpl<ActionHandler *> &Actions);
void createUnwindMapEntry(int ToState, ActionHandler *AH);
- void proccessCallSite(ArrayRef<ActionHandler *> Actions, ImmutableCallSite CS);
+ void createTryBlockMapEntry(int TryLow, int TryHigh,
+ ArrayRef<CatchHandler *> Handlers);
+ void processCallSite(ArrayRef<ActionHandler *> Actions, ImmutableCallSite CS);
void calculateStateNumbers(const Function &F);
};
}
MBBMap[Invoke->getSuccessor(1)]->setIsLandingPad();
// Calculate EH numbers for WinEH.
- if (fn.getFnAttribute("wineh-parent").getValueAsString() == fn.getName())
- WinEHNumbering(MMI.getWinEHFuncInfo(&fn)).calculateStateNumbers(fn);
+ if (fn.getFnAttribute("wineh-parent").getValueAsString() == fn.getName()) {
+ WinEHNumbering Num(MMI.getWinEHFuncInfo(&fn));
+ Num.calculateStateNumbers(fn);
+ // Pop everything on the handler stack.
+ Num.processCallSite(None, ImmutableCallSite());
+ }
}
void WinEHNumbering::parseEHActions(const IntrinsicInst *II,
void WinEHNumbering::createUnwindMapEntry(int ToState, ActionHandler *AH) {
WinEHUnwindMapEntry UME;
UME.ToState = ToState;
- if (auto *CH = dyn_cast<CleanupHandler>(AH))
+ if (auto *CH = dyn_cast_or_null<CleanupHandler>(AH))
UME.Cleanup = cast<Function>(CH->getHandlerBlockOrFunc());
else
UME.Cleanup = nullptr;
FuncInfo.UnwindMap.push_back(UME);
}
+void WinEHNumbering::createTryBlockMapEntry(int TryLow, int TryHigh,
+ ArrayRef<CatchHandler *> Handlers) {
+ WinEHTryBlockMapEntry TBME;
+ TBME.TryLow = TryLow;
+ TBME.TryHigh = TryHigh;
+ // FIXME: This should be revisited when we want to throw inside a catch
+ // handler.
+ TBME.CatchHigh = INT_MAX;
+ assert(TBME.TryLow <= TBME.TryHigh);
+ assert(TBME.CatchHigh > TBME.TryHigh);
+ for (CatchHandler *CH : Handlers) {
+ WinEHHandlerType HT;
+ auto *GV = cast<GlobalVariable>(CH->getSelector()->stripPointerCasts());
+ // Selectors are always pointers to GlobalVariables with 'struct' type.
+ // The struct has two fields, adjectives and a type descriptor.
+ auto *CS = cast<ConstantStruct>(GV->getInitializer());
+ HT.Adjectives =
+ cast<ConstantInt>(CS->getAggregateElement(0U))->getZExtValue();
+ HT.TypeDescriptor = cast<GlobalVariable>(
+ CS->getAggregateElement(1)->stripPointerCasts());
+ HT.Handler = cast<Function>(CH->getHandlerBlockOrFunc());
+ // FIXME: We don't support catching objects yet!
+ HT.CatchObjIdx = INT_MAX;
+ HT.CatchObjOffset = 0;
+ TBME.HandlerArray.push_back(HT);
+ }
+ FuncInfo.TryBlockMap.push_back(TBME);
+}
+
static void print_name(const Value *V) {
#ifndef NDEBUG
if (!V) {
#endif
}
-void WinEHNumbering::proccessCallSite(ArrayRef<ActionHandler *> Actions,
- ImmutableCallSite CS) {
- // float, int
- // float, double, int
+void WinEHNumbering::processCallSite(ArrayRef<ActionHandler *> Actions,
+ ImmutableCallSite CS) {
int FirstMismatch = 0;
for (int E = std::min(HandlerStack.size(), Actions.size()); FirstMismatch < E;
++FirstMismatch) {
delete Actions[FirstMismatch];
}
+ bool EnteringScope = (int)Actions.size() > FirstMismatch;
+ bool ExitingScope = (int)HandlerStack.size() > FirstMismatch;
+
// Don't recurse while we are looping over the handler stack. Instead, defer
// the numbering of the catch handlers until we are done popping.
- SmallVector<const Function *, 4> UnnumberedHandlers;
+ SmallVector<CatchHandler *, 4> PoppedCatches;
for (int I = HandlerStack.size() - 1; I >= FirstMismatch; --I) {
- if (auto *CH = dyn_cast<CatchHandler>(HandlerStack.back()))
- if (const auto *F = dyn_cast<Function>(CH->getHandlerBlockOrFunc()))
- UnnumberedHandlers.push_back(F);
- // Pop the handlers off of the stack.
- delete HandlerStack.back();
+ if (auto *CH = dyn_cast<CatchHandler>(HandlerStack.back())) {
+ PoppedCatches.push_back(CH);
+ } else {
+ // Delete cleanup handlers
+ delete HandlerStack.back();
+ }
HandlerStack.pop_back();
}
- for (const Function *F : UnnumberedHandlers)
- calculateStateNumbers(*F);
+ // We need to create a new state number if we are exiting a try scope and we
+ // will not push any more actions.
+ int TryHigh = NextState - 1;
+ if (ExitingScope && !EnteringScope && !PoppedCatches.empty()) {
+ createUnwindMapEntry(currentEHNumber(), nullptr);
+ ++NextState;
+ }
+
+ int LastTryLowIdx = 0;
+ for (int I = 0, E = PoppedCatches.size(); I != E; ++I) {
+ CatchHandler *CH = PoppedCatches[I];
+ if (I + 1 == E || CH->getEHState() != PoppedCatches[I + 1]->getEHState()) {
+ int TryLow = CH->getEHState();
+ auto Handlers =
+ makeArrayRef(&PoppedCatches[LastTryLowIdx], I - LastTryLowIdx + 1);
+ createTryBlockMapEntry(TryLow, TryHigh, Handlers);
+ LastTryLowIdx = I + 1;
+ }
+ }
+ for (CatchHandler *CH : PoppedCatches) {
+ if (auto *F = dyn_cast<Function>(CH->getHandlerBlockOrFunc()))
+ calculateStateNumbers(*F);
+ delete CH;
+ }
+
+ bool LastActionWasCatch = false;
for (size_t I = FirstMismatch; I != Actions.size(); ++I) {
- createUnwindMapEntry(currentEHNumber(), Actions[I]);
- Actions[I]->setEHState(NextState++);
- DEBUG(dbgs() << "Creating unwind map entry for: (");
- print_name(Actions[I]->getHandlerBlockOrFunc());
- DEBUG(dbgs() << ", " << currentEHNumber() << ")\n");
+ // We can reuse eh states when pushing two catches for the same invoke.
+ bool CurrActionIsCatch = isa<CatchHandler>(Actions[I]);
+ // FIXME: Reenable this optimization!
+ if (CurrActionIsCatch && LastActionWasCatch && false) {
+ Actions[I]->setEHState(currentEHNumber());
+ } else {
+ createUnwindMapEntry(currentEHNumber(), Actions[I]);
+ Actions[I]->setEHState(NextState);
+ NextState++;
+ DEBUG(dbgs() << "Creating unwind map entry for: (");
+ print_name(Actions[I]->getHandlerBlockOrFunc());
+ DEBUG(dbgs() << ", " << currentEHNumber() << ")\n");
+ }
HandlerStack.push_back(Actions[I]);
+ LastActionWasCatch = CurrActionIsCatch;
}
DEBUG(dbgs() << "In EHState " << currentEHNumber() << " for CallSite: ");
}
void WinEHNumbering::calculateStateNumbers(const Function &F) {
+ auto I = VisitedHandlers.insert(&F);
+ if (!I.second)
+ return; // We've already visited this handler, don't renumber it.
+
DEBUG(dbgs() << "Calculating state numbers for: " << F.getName() << '\n');
SmallVector<ActionHandler *, 4> ActionList;
for (const BasicBlock &BB : F) {
const auto *CI = dyn_cast<CallInst>(&I);
if (!CI || CI->doesNotThrow())
continue;
- proccessCallSite(None, CI);
+ processCallSite(None, CI);
}
const auto *II = dyn_cast<InvokeInst>(BB.getTerminator());
if (!II)
continue;
const LandingPadInst *LPI = II->getLandingPadInst();
- if (auto *ActionsCall = dyn_cast<IntrinsicInst>(LPI->getNextNode())) {
- assert(ActionsCall->getIntrinsicID() == Intrinsic::eh_actions);
- parseEHActions(ActionsCall, ActionList);
- proccessCallSite(ActionList, II);
- ActionList.clear();
- FuncInfo.LandingPadStateMap[LPI] = currentEHNumber();
- }
+ auto *ActionsCall = dyn_cast<IntrinsicInst>(LPI->getNextNode());
+ if (!ActionsCall)
+ continue;
+ assert(ActionsCall->getIntrinsicID() == Intrinsic::eh_actions);
+ parseEHActions(ActionsCall, ActionList);
+ processCallSite(ActionList, II);
+ ActionList.clear();
+ FuncInfo.LandingPadStateMap[LPI] = currentEHNumber();
}
- proccessCallSite(None, ImmutableCallSite());
}
/// clear - Clear out all the function-specific state. This returns this
case Intrinsic::eh_begincatch:
case Intrinsic::eh_endcatch:
llvm_unreachable("begin/end catch intrinsics not lowered in codegen");
- case Intrinsic::eh_parentframe: {
- AllocaInst *Slot =
- cast<AllocaInst>(I.getArgOperand(0)->stripPointerCasts());
- assert(FuncInfo.StaticAllocaMap.count(Slot) &&
- "can only use static allocas with llvm.eh.parentframe");
- int FI = FuncInfo.StaticAllocaMap[Slot];
- MachineFunction &MF = DAG.getMachineFunction();
- const Function *F = MF.getFunction();
- MachineModuleInfo &MMI = MF.getMMI();
- MMI.getWinEHFuncInfo(F).CatchHandlerParentFrameObjIdx[F] = FI;
- return nullptr;
- }
case Intrinsic::eh_unwindhelp: {
AllocaInst *Slot =
cast<AllocaInst>(I.getArgOperand(0)->stripPointerCasts());
if (!LPadMap.isInitialized())
LPadMap.mapLandingPad(LPad);
if (auto *CatchAction = dyn_cast<CatchHandler>(Action)) {
- // Insert an alloca for the object which holds the address of the parent's
- // frame pointer. The stack offset of this object needs to be encoded in
- // xdata.
- AllocaInst *ParentFrame = new AllocaInst(Int8PtrType, "parentframe", Entry);
- Builder.CreateStore(&Handler->getArgumentList().back(), ParentFrame,
- /*isStore=*/true);
- Function *ParentFrameFn =
- Intrinsic::getDeclaration(M, Intrinsic::eh_parentframe);
- Builder.CreateCall(ParentFrameFn, ParentFrame);
-
Constant *Sel = CatchAction->getSelector();
Director.reset(new WinEHCatchDirector(Handler, Sel, VarInfo, LPadMap));
LPadMap.remapSelector(VMap, ConstantInt::get(Type::getInt32Ty(Context), 1));
break;
}
- case Intrinsic::eh_parentframe: {
- auto *AI = dyn_cast<AllocaInst>(CI.getArgOperand(0)->stripPointerCasts());
- Assert(AI && AI->isStaticAlloca(),
- "llvm.eh.parentframe requires a static alloca", &CI);
- break;
- }
-
case Intrinsic::eh_unwindhelp: {
auto *AI = dyn_cast<AllocaInst>(CI.getArgOperand(0)->stripPointerCasts());
Assert(AI && AI->isStaticAlloca(),
"$frame_escape_" + Twine(Idx));
}
+MCSymbol *MCContext::getOrCreateParentFrameOffsetSymbol(StringRef FuncName) {
+ return GetOrCreateSymbol(Twine(MAI->getPrivateGlobalPrefix()) + FuncName +
+ "$parent_frame_offset");
+}
+
MCSymbol *MCContext::CreateSymbol(StringRef Name, bool AlwaysAddSuffix) {
// Determine whether this is an assembler temporary or normal label, if used.
bool IsTemporary = false;
#include "llvm/CodeGen/MachineJumpTableInfo.h"
#include "llvm/CodeGen/MachineModuleInfo.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
+#include "llvm/CodeGen/WinEHFuncInfo.h"
#include "llvm/IR/CallSite.h"
#include "llvm/IR/CallingConv.h"
#include "llvm/IR/Constants.h"
return makeArrayRef(std::begin(XMMArgRegs64Bit), std::end(XMMArgRegs64Bit));
}
+static bool isOutlinedHandler(const MachineFunction &MF) {
+ const MachineModuleInfo &MMI = MF.getMMI();
+ const Function *F = MF.getFunction();
+ return MMI.getWinEHParent(F) != F;
+}
+
SDValue
X86TargetLowering::LowerFormalArguments(SDValue Chain,
CallingConv::ID CallConv,
const {
MachineFunction &MF = DAG.getMachineFunction();
X86MachineFunctionInfo *FuncInfo = MF.getInfo<X86MachineFunctionInfo>();
+ const TargetFrameLowering &TFI = *Subtarget->getFrameLowering();
const Function* Fn = MF.getFunction();
if (Fn->hasExternalLinkage() &&
}
if (IsWin64) {
- const TargetFrameLowering &TFI = *Subtarget->getFrameLowering();
// Get to the caller-allocated home save location. Add 8 to account
// for the return address.
int HomeOffset = TFI.getOffsetOfLocalArea() + 8;
if (!MemOps.empty())
Chain = DAG.getNode(ISD::TokenFactor, dl, MVT::Other, MemOps);
+ } else if (IsWin64 && isOutlinedHandler(MF)) {
+ // Get to the caller-allocated home save location. Add 8 to account
+ // for the return address.
+ int HomeOffset = TFI.getOffsetOfLocalArea() + 8;
+ FuncInfo->setRegSaveFrameIndex(MFI->CreateFixedObject(
+ /*Size=*/1, /*SPOffset=*/HomeOffset + 8, /*Immutable=*/false));
+
+ MachineModuleInfo &MMI = MF.getMMI();
+ MMI.getWinEHFuncInfo(Fn)
+ .CatchHandlerParentFrameObjIdx[const_cast<Function *>(Fn)] =
+ FuncInfo->getRegSaveFrameIndex();
+
+ // Store the second integer parameter (rdx) into rsp+16 relative to the
+ // stack pointer at the entry of the function.
+ SDValue RSFIN =
+ DAG.getFrameIndex(FuncInfo->getRegSaveFrameIndex(), getPointerTy());
+ unsigned GPR = MF.addLiveIn(X86::RDX, &X86::GR64RegClass);
+ SDValue Val = DAG.getCopyFromReg(Chain, dl, GPR, MVT::i64);
+ Chain = DAG.getStore(
+ Val.getValue(1), dl, Val, RSFIN,
+ MachinePointerInfo::getFixedStack(FuncInfo->getRegSaveFrameIndex()),
+ /*isVolatile=*/true, /*isNonTemporal=*/false, /*Alignment=*/0);
}
if (isVarArg && MFI->hasMustTailInVarArgFunc()) {
; CHECK-LABEL: define internal i8* @"\01?test@@YAXXZ.catch"(i8*, i8*)
; CHECK: entry:
-; CHECK: [[PARENTFRAME:\%.+]] = alloca i8*
-; CHECK: store volatile i8* %1, i8** [[PARENTFRAME]]
-; CHECK: call void @llvm.eh.parentframe(i8** [[PARENTFRAME]])
; CHECK: [[RECOVER_TMP1:\%.+]] = call i8* @llvm.framerecover(i8* bitcast (void ()* @"\01?test@@YAXXZ" to i8*), i8* %1, i32 0)
; CHECK: [[TMP1_PTR:\%.+]] = bitcast i8* [[RECOVER_TMP1]] to i32*
; CHECK: call void @"\01?handle_exception@@YAXXZ"()
; CHECK-LABEL: define internal i8* @"\01?test@@YAXXZ.catch1"(i8*, i8*)
; CHECK: entry:
-; CHECK: [[PARENTFRAME:\%.+]] = alloca i8*
-; CHECK: store volatile i8* %1, i8** [[PARENTFRAME]]
-; CHECK: call void @llvm.eh.parentframe(i8** [[PARENTFRAME]])
; CHECK: [[RECOVER_TMP0:\%.+]] = call i8* @llvm.framerecover(i8* bitcast (void ()* @"\01?test@@YAXXZ" to i8*), i8* %1, i32 2)
; CHECK: [[TMP0_PTR:\%.+]] = bitcast i8* [[RECOVER_TMP0]] to i32*
; CHECK: invoke void @"\01?handle_exception@@YAXXZ"()
+++ /dev/null
-; RUN: llc < %s | FileCheck %s
-
-target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128"
-target triple = "x86_64-pc-windows-msvc"
-
-%rtti.TypeDescriptor2 = type { i8**, i8*, [3 x i8] }
-%eh.CatchableType = type { i32, i32, i32, i32, i32, i32, i32 }
-%eh.CatchableTypeArray.1 = type { i32, [1 x i32] }
-%eh.ThrowInfo = type { i32, i32, i32, i32 }
-%struct.S = type { i8 }
-
-$"\01??_DS@@QEAA@XZ" = comdat any
-
-$"\01??_R0H@8" = comdat any
-
-$"_CT??_R0H@84" = comdat any
-
-$_CTA1H = comdat any
-
-$_TI1H = comdat any
-
-@"\01??_7type_info@@6B@" = external constant i8*
-@"\01??_R0H@8" = linkonce_odr global %rtti.TypeDescriptor2 { i8** @"\01??_7type_info@@6B@", i8* null, [3 x i8] c".H\00" }, comdat
-@__ImageBase = external constant i8
-@"_CT??_R0H@84" = linkonce_odr unnamed_addr constant %eh.CatchableType { i32 1, i32 trunc (i64 sub nuw nsw (i64 ptrtoint (%rtti.TypeDescriptor2* @"\01??_R0H@8" to i64), i64 ptrtoint (i8* @__ImageBase to i64)) to i32), i32 0, i32 -1, i32 0, i32 4, i32 0 }, section ".xdata", comdat
-@_CTA1H = linkonce_odr unnamed_addr constant %eh.CatchableTypeArray.1 { i32 1, [1 x i32] [i32 trunc (i64 sub nuw nsw (i64 ptrtoint (%eh.CatchableType* @"_CT??_R0H@84" to i64), i64 ptrtoint (i8* @__ImageBase to i64)) to i32)] }, section ".xdata", comdat
-@_TI1H = linkonce_odr unnamed_addr constant %eh.ThrowInfo { i32 0, i32 0, i32 0, i32 trunc (i64 sub nuw nsw (i64 ptrtoint (%eh.CatchableTypeArray.1* @_CTA1H to i64), i64 ptrtoint (i8* @__ImageBase to i64)) to i32) }, section ".xdata", comdat
-
-
-; CHECK-LABEL: ?test1@@YAXXZ":
-; CHECK: .seh_handlerdata
-; CHECK-NEXT: .long ("$cppxdata$?test1@@YAXXZ")@IMGREL
-; CHECK-NEXT:"$cppxdata$?test1@@YAXXZ":
-; CHECK-NEXT: .long 429065506
-; CHECK-NEXT: .long 1
-; CHECK-NEXT: .long ("$stateUnwindMap$?test1@@YAXXZ")@IMGREL
-; CHECK-NEXT: .long 0
-; CHECK-NEXT: .long 0
-; CHECK-NEXT: .long 1
-; CHECK-NEXT: .long ("$ip2state$?test1@@YAXXZ")@IMGREL
-; CHECK-NEXT: .long 64
-; CHECK-NEXT: .long 0
-; CHECK-NEXT: .long 1
-; CHECK-NEXT:"$stateUnwindMap$?test1@@YAXXZ":
-; CHECK-NEXT: .long -1
-; CHECK-NEXT: .long "?test1@@YAXXZ.cleanup"@IMGREL
-; CHECK-NEXT:"$ip2state$?test1@@YAXXZ":
-; CHECK-NEXT: .long .Ltmp0@IMGREL
-; CHECK-NEXT: .long 0
-
-define void @"\01?test1@@YAXXZ"() #0 {
-entry:
- %unwindhelp = alloca i64
- %tmp = alloca i32, align 4
- %exn.slot = alloca i8*
- %ehselector.slot = alloca i32
- store i32 0, i32* %tmp
- %0 = bitcast i32* %tmp to i8*
- call void (...)* @llvm.frameescape()
- store volatile i64 -2, i64* %unwindhelp
- %1 = bitcast i64* %unwindhelp to i8*
- call void @llvm.eh.unwindhelp(i8* %1)
- invoke void @_CxxThrowException(i8* %0, %eh.ThrowInfo* @_TI1H) #8
- to label %unreachable unwind label %lpad1
-
-lpad1: ; preds = %entry
- %2 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*)
- cleanup
- %recover = call i8* (...)* @llvm.eh.actions(i32 0, void (i8*, i8*)* @"\01?test1@@YAXXZ.cleanup")
- indirectbr i8* %recover, []
-
-unreachable: ; preds = %entry
- unreachable
-}
-
-declare void @_CxxThrowException(i8*, %eh.ThrowInfo*)
-
-declare i32 @__CxxFrameHandler3(...)
-
-; Function Attrs: nounwind
-define linkonce_odr void @"\01??_DS@@QEAA@XZ"(%struct.S* %this) unnamed_addr #1 comdat align 2 {
-entry:
- %this.addr = alloca %struct.S*, align 8
- store %struct.S* %this, %struct.S** %this.addr, align 8
- %this1 = load %struct.S*, %struct.S** %this.addr
- call void @"\01??1S@@QEAA@XZ"(%struct.S* %this1) #4
- ret void
-}
-
-; CHECK-LABEL: "?test2@@YAX_N@Z":
-; CHECK: .seh_handlerdata
-; CHECK-NEXT: .long ("$cppxdata$?test2@@YAX_N@Z")@IMGREL
-; CHECK-NEXT:"$cppxdata$?test2@@YAX_N@Z":
-; CHECK-NEXT: .long 429065506
-; CHECK-NEXT: .long 2
-; CHECK-NEXT: .long ("$stateUnwindMap$?test2@@YAX_N@Z")@IMGREL
-; CHECK-NEXT: .long 0
-; CHECK-NEXT: .long 0
-; CHECK-NEXT: .long 4
-; CHECK-NEXT: .long ("$ip2state$?test2@@YAX_N@Z")@IMGREL
-; CHECK-NEXT: .long 64
-; CHECK-NEXT: .long 0
-; CHECK-NEXT: .long 1
-; CHECK-NEXT:"$stateUnwindMap$?test2@@YAX_N@Z":
-; CHECK-NEXT: .long -1
-; CHECK-NEXT: .long "?test2@@YAX_N@Z.cleanup"@IMGREL
-; CHECK-NEXT: .long 0
-; CHECK-NEXT: .long "?test2@@YAX_N@Z.cleanup1"@IMGREL
-; CHECK-NEXT:"$ip2state$?test2@@YAX_N@Z":
-; CHECK-NEXT: .long .Lfunc_begin1@IMGREL
-; CHECK-NEXT: .long -1
-; CHECK-NEXT: .long .Ltmp7@IMGREL
-; CHECK-NEXT: .long 0
-; CHECK-NEXT: .long .Ltmp9@IMGREL
-; CHECK-NEXT: .long 1
-; CHECK-NEXT: .long .Ltmp12@IMGREL
-; CHECK-NEXT: .long 0
-
-define void @"\01?test2@@YAX_N@Z"(i1 zeroext %b) #2 {
-entry:
- %unwindhelp = alloca i64
- %b.addr = alloca i8, align 1
- %s = alloca %struct.S, align 1
- %exn.slot = alloca i8*
- %ehselector.slot = alloca i32
- %s1 = alloca %struct.S, align 1
- %frombool = zext i1 %b to i8
- store i8 %frombool, i8* %b.addr, align 1
- call void @"\01?may_throw@@YAXXZ"()
- call void (...)* @llvm.frameescape(%struct.S* %s, %struct.S* %s1)
- store volatile i64 -2, i64* %unwindhelp
- %0 = bitcast i64* %unwindhelp to i8*
- call void @llvm.eh.unwindhelp(i8* %0)
- invoke void @"\01?may_throw@@YAXXZ"()
- to label %invoke.cont unwind label %lpad1
-
-invoke.cont: ; preds = %entry
- %1 = load i8, i8* %b.addr, align 1
- %tobool = trunc i8 %1 to i1
- br i1 %tobool, label %if.then, label %if.else
-
-if.then: ; preds = %invoke.cont
- invoke void @"\01?may_throw@@YAXXZ"()
- to label %invoke.cont3 unwind label %lpad3
-
-invoke.cont3: ; preds = %if.then
- call void @"\01??_DS@@QEAA@XZ"(%struct.S* %s1) #4
- br label %if.end
-
-lpad1: ; preds = %entry, %if.end
- %2 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*)
- cleanup
- %recover = call i8* (...)* @llvm.eh.actions(i32 0, void (i8*, i8*)* @"\01?test2@@YAX_N@Z.cleanup")
- indirectbr i8* %recover, []
-
-lpad3: ; preds = %if.then
- %3 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*)
- cleanup
- %recover4 = call i8* (...)* @llvm.eh.actions(i32 0, void (i8*, i8*)* @"\01?test2@@YAX_N@Z.cleanup1", i32 0, void (i8*, i8*)* @"\01?test2@@YAX_N@Z.cleanup")
- indirectbr i8* %recover4, []
-
-if.else: ; preds = %invoke.cont
- call void @"\01?dont_throw@@YAXXZ"() #4
- br label %if.end
-
-if.end: ; preds = %if.else, %invoke.cont3
- invoke void @"\01?may_throw@@YAXXZ"()
- to label %invoke.cont4 unwind label %lpad1
-
-invoke.cont4: ; preds = %if.end
- call void @"\01??_DS@@QEAA@XZ"(%struct.S* %s) #4
- ret void
-}
-
-declare void @"\01?may_throw@@YAXXZ"() #3
-
-; Function Attrs: nounwind
-declare void @"\01?dont_throw@@YAXXZ"() #1
-
-; Function Attrs: nounwind
-declare void @"\01??1S@@QEAA@XZ"(%struct.S*) #1
-
-; Function Attrs: nounwind
-declare i8* @llvm.eh.actions(...) #4
-
-define internal void @"\01?test1@@YAXXZ.cleanup"(i8*, i8*) #5 {
-entry:
- %s = alloca %struct.S, align 1
- call void @"\01??_DS@@QEAA@XZ"(%struct.S* %s) #4
- ret void
-}
-
-; Function Attrs: nounwind
-declare void @llvm.frameescape(...) #4
-
-; Function Attrs: nounwind readnone
-declare i8* @llvm.framerecover(i8*, i8*, i32) #6
-
-; Function Attrs: nounwind
-declare void @llvm.eh.unwindhelp(i8*) #4
-
-define internal void @"\01?test2@@YAX_N@Z.cleanup"(i8*, i8*) #7 {
-entry:
- %s.i8 = call i8* @llvm.framerecover(i8* bitcast (void (i1)* @"\01?test2@@YAX_N@Z" to i8*), i8* %1, i32 0)
- %s = bitcast i8* %s.i8 to %struct.S*
- call void @"\01??_DS@@QEAA@XZ"(%struct.S* %s) #4
- ret void
-}
-
-define internal void @"\01?test2@@YAX_N@Z.cleanup1"(i8*, i8*) #7 {
-entry:
- %s1.i8 = call i8* @llvm.framerecover(i8* bitcast (void (i1)* @"\01?test2@@YAX_N@Z" to i8*), i8* %1, i32 1)
- %s1 = bitcast i8* %s1.i8 to %struct.S*
- call void @"\01??_DS@@QEAA@XZ"(%struct.S* %s1) #4
- ret void
-}
-
-attributes #0 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-realign-stack" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" "wineh-parent"="\ 1?test1@@YAXXZ" }
-attributes #1 = { nounwind "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-realign-stack" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" }
-attributes #2 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-realign-stack" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" "wineh-parent"="\ 1?test2@@YAX_N@Z" }
-attributes #3 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-realign-stack" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" }
-attributes #4 = { nounwind }
-attributes #5 = { "wineh-parent"="\ 1?test1@@YAXXZ" }
-attributes #6 = { nounwind readnone }
-attributes #7 = { "wineh-parent"="\ 1?test2@@YAX_N@Z" }
-attributes #8 = { noreturn }
--- /dev/null
+; RUN: llc < %s | FileCheck %s
+
+target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-pc-windows-msvc"
+
+; This test case is equivalent to:
+; void f() {
+; try {
+; try {
+; may_throw();
+; } catch (int &) {
+; may_throw();
+; }
+; may_throw();
+; } catch (double) {
+; }
+; }
+
+
+%rtti.TypeDescriptor2 = type { i8**, i8*, [3 x i8] }
+%eh.CatchHandlerType = type { i32, i8* }
+
+$"\01??_R0N@8" = comdat any
+
+$"\01??_R0H@8" = comdat any
+
+@"\01??_7type_info@@6B@" = external constant i8*
+@"\01??_R0N@8" = linkonce_odr global %rtti.TypeDescriptor2 { i8** @"\01??_7type_info@@6B@", i8* null, [3 x i8] c".N\00" }, comdat
+@llvm.eh.handlertype.N.0 = private unnamed_addr constant %eh.CatchHandlerType { i32 0, i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0N@8" to i8*) }, section "llvm.metadata"
+@"\01??_R0H@8" = linkonce_odr global %rtti.TypeDescriptor2 { i8** @"\01??_7type_info@@6B@", i8* null, [3 x i8] c".H\00" }, comdat
+@llvm.eh.handlertype.H.8 = private unnamed_addr constant %eh.CatchHandlerType { i32 8, i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0H@8" to i8*) }, section "llvm.metadata"
+
+define internal i8* @"\01?f@@YAXXZ.catch"(i8*, i8*) #4 {
+entry:
+ %.i8 = call i8* @llvm.framerecover(i8* bitcast (void ()* @"\01?f@@YAXXZ" to i8*), i8* %1, i32 0)
+ %2 = bitcast i8* %.i8 to i32**
+ %3 = bitcast i32** %2 to i8*
+ invoke void @"\01?may_throw@@YAXXZ"()
+ to label %invoke.cont2 unwind label %lpad1
+
+invoke.cont2: ; preds = %entry
+ ret i8* blockaddress(@"\01?f@@YAXXZ", %try.cont)
+
+lpad1: ; preds = %entry
+ %4 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*)
+ cleanup
+ catch %eh.CatchHandlerType* @llvm.eh.handlertype.N.0
+ %recover = call i8* (...)* @llvm.eh.actions(i32 1, i8* bitcast (%eh.CatchHandlerType* @llvm.eh.handlertype.N.0 to i8*), double* null, i8* (i8*, i8*)* @"\01?f@@YAXXZ.catch1")
+ indirectbr i8* %recover, [label %invoke.cont2]
+}
+
+; CHECK-LABEL: "?f@@YAXXZ.catch":
+; CHECK: ".L?f@@YAXXZ.catch$parent_frame_offset" = 56
+; CHECK: movq %rdx, 56(%rsp)
+; CHECK: .seh_handlerdata
+; CHECK: .long ("$cppxdata$?f@@YAXXZ")@IMGREL
+
+
+define internal i8* @"\01?f@@YAXXZ.catch1"(i8*, i8*) #4 {
+entry:
+ %.i8 = call i8* @llvm.framerecover(i8* bitcast (void ()* @"\01?f@@YAXXZ" to i8*), i8* %1, i32 1)
+ %2 = bitcast i8* %.i8 to double*
+ %3 = bitcast double* %2 to i8*
+ invoke void (...)* @llvm.donothing()
+ to label %done unwind label %lpad
+
+done:
+ ret i8* blockaddress(@"\01?f@@YAXXZ", %try.cont8)
+
+lpad: ; preds = %entry
+ %4 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*)
+ cleanup
+ unreachable
+}
+
+; CHECK-LABEL: "?f@@YAXXZ.catch1":
+; CHECK: ".L?f@@YAXXZ.catch1$parent_frame_offset" = 16
+; CHECK: movq %rdx, 16(%rsp)
+; CHECK: .seh_handlerdata
+; CHECK: .long ("$cppxdata$?f@@YAXXZ")@IMGREL
+
+define void @"\01?f@@YAXXZ"() #0 {
+entry:
+ %unwindhelp = alloca i64
+ %exn.slot = alloca i8*
+ %ehselector.slot = alloca i32
+ %0 = alloca i32*, align 8
+ %1 = alloca double, align 8
+ call void (...)* @llvm.frameescape(i32** %0, double* %1)
+ store volatile i64 -2, i64* %unwindhelp
+ %2 = bitcast i64* %unwindhelp to i8*
+ call void @llvm.eh.unwindhelp(i8* %2)
+ invoke void @"\01?may_throw@@YAXXZ"()
+ to label %invoke.cont unwind label %lpad2
+
+invoke.cont: ; preds = %entry
+ br label %try.cont
+
+lpad2: ; preds = %entry
+ %3 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*)
+ catch %eh.CatchHandlerType* @llvm.eh.handlertype.H.8
+ catch %eh.CatchHandlerType* @llvm.eh.handlertype.N.0
+ %recover = call i8* (...)* @llvm.eh.actions(i32 1, i8* bitcast (%eh.CatchHandlerType* @llvm.eh.handlertype.H.8 to i8*), i32** %0, i8* (i8*, i8*)* @"\01?f@@YAXXZ.catch", i32 1, i8* bitcast (%eh.CatchHandlerType* @llvm.eh.handlertype.N.0 to i8*), double* %1, i8* (i8*, i8*)* @"\01?f@@YAXXZ.catch1")
+ indirectbr i8* %recover, [label %try.cont, label %try.cont8]
+
+try.cont: ; preds = %lpad2, %invoke.cont
+ invoke void @"\01?may_throw@@YAXXZ"()
+ to label %try.cont8 unwind label %lpad1
+
+lpad1:
+ %4 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*)
+ catch %eh.CatchHandlerType* @llvm.eh.handlertype.N.0
+ %recover2 = call i8* (...)* @llvm.eh.actions(i32 1, i8* bitcast (%eh.CatchHandlerType* @llvm.eh.handlertype.N.0 to i8*), double* null, i8* (i8*, i8*)* @"\01?f@@YAXXZ.catch1")
+ indirectbr i8* %recover2, [label %try.cont8]
+
+try.cont8: ; preds = %lpad2, %try.cont
+ ret void
+}
+
+; CHECK-LABEL: "?f@@YAXXZ":
+; CHECK: .seh_handlerdata
+; CHECK-NEXT: .long ("$cppxdata$?f@@YAXXZ")@IMGREL
+; CHECK-NEXT:"$cppxdata$?f@@YAXXZ":
+; CHECK-NEXT: .long 429065506
+; CHECK-NEXT: .long 4
+; CHECK-NEXT: .long ("$stateUnwindMap$?f@@YAXXZ")@IMGREL
+; CHECK-NEXT: .long 2
+; CHECK-NEXT: .long ("$tryMap$?f@@YAXXZ")@IMGREL
+; CHECK-NEXT: .long 3
+; CHECK-NEXT: .long ("$ip2state$?f@@YAXXZ")@IMGREL
+; CHECK-NEXT: .long 64
+; CHECK-NEXT: .long 0
+; CHECK-NEXT: .long 1
+; CHECK-NEXT:"$stateUnwindMap$?f@@YAXXZ":
+; CHECK-NEXT: .long -1
+; CHECK-NEXT: .long 0
+; CHECK-NEXT: .long 0
+; CHECK-NEXT: .long 0
+; CHECK-NEXT: .long 0
+; CHECK-NEXT: .long 0
+; CHECK-NEXT: .long -1
+; CHECK-NEXT: .long 0
+; CHECK-NEXT:"$tryMap$?f@@YAXXZ":
+; CHECK-NEXT: .long 1
+; CHECK-NEXT: .long 1
+; CHECK-NEXT: .long 2147483647
+; CHECK-NEXT: .long 1
+; CHECK-NEXT: .long ("$handlerMap$0$?f@@YAXXZ")@IMGREL
+; CHECK-NEXT: .long 0
+; CHECK-NEXT: .long 2
+; CHECK-NEXT: .long 2147483647
+; CHECK-NEXT: .long 1
+; CHECK-NEXT: .long ("$handlerMap$1$?f@@YAXXZ")@IMGREL
+; CHECK-NEXT:"$handlerMap$0$?f@@YAXXZ":
+; CHECK-NEXT: .long 8
+; CHECK-NEXT: .long "??_R0H@8"@IMGREL
+; CHECK-NEXT: .long 0
+; CHECK-NEXT: .long "?f@@YAXXZ.catch"@IMGREL
+; CHECK-NEXT: .long ".L?f@@YAXXZ.catch$parent_frame_offset"
+; CHECK-NEXT:"$handlerMap$1$?f@@YAXXZ":
+; CHECK-NEXT: .long 0
+; CHECK-NEXT: .long "??_R0N@8"@IMGREL
+; CHECK-NEXT: .long 0
+; CHECK-NEXT: .long "?f@@YAXXZ.catch1"@IMGREL
+; CHECK-NEXT: .long ".L?f@@YAXXZ.catch1$parent_frame_offset"
+; CHECK-NEXT:"$ip2state$?f@@YAXXZ":
+; CHECK-NEXT: .long .Ltmp0@IMGREL
+; CHECK-NEXT: .long 0
+; CHECK-NEXT: .long .Ltmp13@IMGREL
+; CHECK-NEXT: .long 1
+; CHECK-NEXT: .long .Ltmp16@IMGREL
+; CHECK-NEXT: .long 0
+
+
+declare void @"\01?may_throw@@YAXXZ"() #1
+
+declare i32 @__CxxFrameHandler3(...)
+
+; Function Attrs: nounwind readnone
+declare i32 @llvm.eh.typeid.for(i8*) #2
+
+; Function Attrs: nounwind
+declare void @llvm.eh.begincatch(i8* nocapture, i8* nocapture) #3
+
+; Function Attrs: nounwind
+declare void @llvm.eh.endcatch() #3
+
+; Function Attrs: nounwind
+declare i8* @llvm.eh.actions(...) #3
+
+; Function Attrs: nounwind
+declare void @llvm.frameescape(...) #3
+
+; Function Attrs: nounwind readnone
+declare i8* @llvm.framerecover(i8*, i8*, i32) #2
+
+; Function Attrs: nounwind
+declare void @llvm.eh.unwindhelp(i8*) #3
+
+declare void @llvm.donothing(...)
+
+attributes #0 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-realign-stack" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" "wineh-parent"="\ 1?f@@YAXXZ" }
+attributes #1 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-realign-stack" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" }
+attributes #2 = { nounwind readnone }
+attributes #3 = { nounwind }
+attributes #4 = { "wineh-parent"="\ 1?f@@YAXXZ" }
--- /dev/null
+; RUN: llc < %s | FileCheck %s
+
+target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-pc-windows-msvc"
+
+%rtti.TypeDescriptor2 = type { i8**, i8*, [3 x i8] }
+%eh.CatchableType = type { i32, i32, i32, i32, i32, i32, i32 }
+%eh.CatchableTypeArray.1 = type { i32, [1 x i32] }
+%eh.ThrowInfo = type { i32, i32, i32, i32 }
+%struct.S = type { i8 }
+
+$"\01??_DS@@QEAA@XZ" = comdat any
+
+$"\01??_R0H@8" = comdat any
+
+$"_CT??_R0H@84" = comdat any
+
+$_CTA1H = comdat any
+
+$_TI1H = comdat any
+
+@"\01??_7type_info@@6B@" = external constant i8*
+@"\01??_R0H@8" = linkonce_odr global %rtti.TypeDescriptor2 { i8** @"\01??_7type_info@@6B@", i8* null, [3 x i8] c".H\00" }, comdat
+@__ImageBase = external constant i8
+@"_CT??_R0H@84" = linkonce_odr unnamed_addr constant %eh.CatchableType { i32 1, i32 trunc (i64 sub nuw nsw (i64 ptrtoint (%rtti.TypeDescriptor2* @"\01??_R0H@8" to i64), i64 ptrtoint (i8* @__ImageBase to i64)) to i32), i32 0, i32 -1, i32 0, i32 4, i32 0 }, section ".xdata", comdat
+@_CTA1H = linkonce_odr unnamed_addr constant %eh.CatchableTypeArray.1 { i32 1, [1 x i32] [i32 trunc (i64 sub nuw nsw (i64 ptrtoint (%eh.CatchableType* @"_CT??_R0H@84" to i64), i64 ptrtoint (i8* @__ImageBase to i64)) to i32)] }, section ".xdata", comdat
+@_TI1H = linkonce_odr unnamed_addr constant %eh.ThrowInfo { i32 0, i32 0, i32 0, i32 trunc (i64 sub nuw nsw (i64 ptrtoint (%eh.CatchableTypeArray.1* @_CTA1H to i64), i64 ptrtoint (i8* @__ImageBase to i64)) to i32) }, section ".xdata", comdat
+
+
+; CHECK-LABEL: "?test1@@YAXXZ":
+; CHECK: .seh_handlerdata
+; CHECK-NEXT: .long ("$cppxdata$?test1@@YAXXZ")@IMGREL
+; CHECK-NEXT:"$cppxdata$?test1@@YAXXZ":
+; CHECK-NEXT: .long 429065506
+; CHECK-NEXT: .long 1
+; CHECK-NEXT: .long ("$stateUnwindMap$?test1@@YAXXZ")@IMGREL
+; CHECK-NEXT: .long 0
+; CHECK-NEXT: .long 0
+; CHECK-NEXT: .long 1
+; CHECK-NEXT: .long ("$ip2state$?test1@@YAXXZ")@IMGREL
+; CHECK-NEXT: .long 64
+; CHECK-NEXT: .long 0
+; CHECK-NEXT: .long 1
+; CHECK-NEXT:"$stateUnwindMap$?test1@@YAXXZ":
+; CHECK-NEXT: .long -1
+; CHECK-NEXT: .long "?test1@@YAXXZ.cleanup"@IMGREL
+; CHECK-NEXT:"$ip2state$?test1@@YAXXZ":
+; CHECK-NEXT: .long .Ltmp0@IMGREL
+; CHECK-NEXT: .long 0
+
+define void @"\01?test1@@YAXXZ"() #0 {
+entry:
+ %unwindhelp = alloca i64
+ %tmp = alloca i32, align 4
+ %exn.slot = alloca i8*
+ %ehselector.slot = alloca i32
+ store i32 0, i32* %tmp
+ %0 = bitcast i32* %tmp to i8*
+ call void (...)* @llvm.frameescape()
+ store volatile i64 -2, i64* %unwindhelp
+ %1 = bitcast i64* %unwindhelp to i8*
+ call void @llvm.eh.unwindhelp(i8* %1)
+ invoke void @_CxxThrowException(i8* %0, %eh.ThrowInfo* @_TI1H) #8
+ to label %unreachable unwind label %lpad1
+
+lpad1: ; preds = %entry
+ %2 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*)
+ cleanup
+ %recover = call i8* (...)* @llvm.eh.actions(i32 0, void (i8*, i8*)* @"\01?test1@@YAXXZ.cleanup")
+ indirectbr i8* %recover, []
+
+unreachable: ; preds = %entry
+ unreachable
+}
+
+declare void @_CxxThrowException(i8*, %eh.ThrowInfo*)
+
+declare i32 @__CxxFrameHandler3(...)
+
+; Function Attrs: nounwind
+define linkonce_odr void @"\01??_DS@@QEAA@XZ"(%struct.S* %this) unnamed_addr #1 comdat align 2 {
+entry:
+ %this.addr = alloca %struct.S*, align 8
+ store %struct.S* %this, %struct.S** %this.addr, align 8
+ %this1 = load %struct.S*, %struct.S** %this.addr
+ call void @"\01??1S@@QEAA@XZ"(%struct.S* %this1) #4
+ ret void
+}
+
+; CHECK-LABEL: "?test2@@YAX_N@Z":
+; CHECK: .seh_handlerdata
+; CHECK-NEXT: .long ("$cppxdata$?test2@@YAX_N@Z")@IMGREL
+; CHECK-NEXT:"$cppxdata$?test2@@YAX_N@Z":
+; CHECK-NEXT: .long 429065506
+; CHECK-NEXT: .long 2
+; CHECK-NEXT: .long ("$stateUnwindMap$?test2@@YAX_N@Z")@IMGREL
+; CHECK-NEXT: .long 0
+; CHECK-NEXT: .long 0
+; CHECK-NEXT: .long 4
+; CHECK-NEXT: .long ("$ip2state$?test2@@YAX_N@Z")@IMGREL
+; CHECK-NEXT: .long 64
+; CHECK-NEXT: .long 0
+; CHECK-NEXT: .long 1
+; CHECK-NEXT:"$stateUnwindMap$?test2@@YAX_N@Z":
+; CHECK-NEXT: .long -1
+; CHECK-NEXT: .long "?test2@@YAX_N@Z.cleanup"@IMGREL
+; CHECK-NEXT: .long 0
+; CHECK-NEXT: .long "?test2@@YAX_N@Z.cleanup1"@IMGREL
+; CHECK-NEXT:"$ip2state$?test2@@YAX_N@Z":
+; CHECK-NEXT: .long .Lfunc_begin1@IMGREL
+; CHECK-NEXT: .long -1
+; CHECK-NEXT: .long .Ltmp7@IMGREL
+; CHECK-NEXT: .long 0
+; CHECK-NEXT: .long .Ltmp9@IMGREL
+; CHECK-NEXT: .long 1
+; CHECK-NEXT: .long .Ltmp12@IMGREL
+; CHECK-NEXT: .long 0
+
+define void @"\01?test2@@YAX_N@Z"(i1 zeroext %b) #2 {
+entry:
+ %unwindhelp = alloca i64
+ %b.addr = alloca i8, align 1
+ %s = alloca %struct.S, align 1
+ %exn.slot = alloca i8*
+ %ehselector.slot = alloca i32
+ %s1 = alloca %struct.S, align 1
+ %frombool = zext i1 %b to i8
+ store i8 %frombool, i8* %b.addr, align 1
+ %0 = bitcast i64* %unwindhelp to i8*
+ store volatile i64 -2, i64* %unwindhelp
+ call void (...)* @llvm.frameescape(%struct.S* %s, %struct.S* %s1)
+ call void @llvm.eh.unwindhelp(i8* %0)
+ call void @"\01?may_throw@@YAXXZ"()
+ invoke void @"\01?may_throw@@YAXXZ"()
+ to label %invoke.cont unwind label %lpad1
+
+invoke.cont: ; preds = %entry
+ %1 = load i8, i8* %b.addr, align 1
+ %tobool = trunc i8 %1 to i1
+ br i1 %tobool, label %if.then, label %if.else
+
+if.then: ; preds = %invoke.cont
+ invoke void @"\01?may_throw@@YAXXZ"()
+ to label %invoke.cont3 unwind label %lpad3
+
+invoke.cont3: ; preds = %if.then
+ call void @"\01??_DS@@QEAA@XZ"(%struct.S* %s1) #4
+ br label %if.end
+
+lpad1: ; preds = %entry, %if.end
+ %2 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*)
+ cleanup
+ %recover = call i8* (...)* @llvm.eh.actions(i32 0, void (i8*, i8*)* @"\01?test2@@YAX_N@Z.cleanup")
+ indirectbr i8* %recover, []
+
+lpad3: ; preds = %if.then
+ %3 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*)
+ cleanup
+ %recover4 = call i8* (...)* @llvm.eh.actions(i32 0, void (i8*, i8*)* @"\01?test2@@YAX_N@Z.cleanup1", i32 0, void (i8*, i8*)* @"\01?test2@@YAX_N@Z.cleanup")
+ indirectbr i8* %recover4, []
+
+if.else: ; preds = %invoke.cont
+ call void @"\01?dont_throw@@YAXXZ"() #4
+ br label %if.end
+
+if.end: ; preds = %if.else, %invoke.cont3
+ invoke void @"\01?may_throw@@YAXXZ"()
+ to label %invoke.cont4 unwind label %lpad1
+
+invoke.cont4: ; preds = %if.end
+ call void @"\01??_DS@@QEAA@XZ"(%struct.S* %s) #4
+ ret void
+}
+
+declare void @"\01?may_throw@@YAXXZ"() #3
+
+; Function Attrs: nounwind
+declare void @"\01?dont_throw@@YAXXZ"() #1
+
+; Function Attrs: nounwind
+declare void @"\01??1S@@QEAA@XZ"(%struct.S*) #1
+
+; Function Attrs: nounwind
+declare i8* @llvm.eh.actions(...) #4
+
+define internal void @"\01?test1@@YAXXZ.cleanup"(i8*, i8*) #5 {
+entry:
+ %s = alloca %struct.S, align 1
+ call void @"\01??_DS@@QEAA@XZ"(%struct.S* %s) #4
+ ret void
+}
+
+; Function Attrs: nounwind
+declare void @llvm.frameescape(...) #4
+
+; Function Attrs: nounwind readnone
+declare i8* @llvm.framerecover(i8*, i8*, i32) #6
+
+; Function Attrs: nounwind
+declare void @llvm.eh.unwindhelp(i8*) #4
+
+define internal void @"\01?test2@@YAX_N@Z.cleanup"(i8*, i8*) #7 {
+entry:
+ %s.i8 = call i8* @llvm.framerecover(i8* bitcast (void (i1)* @"\01?test2@@YAX_N@Z" to i8*), i8* %1, i32 0)
+ %s = bitcast i8* %s.i8 to %struct.S*
+ call void @"\01??_DS@@QEAA@XZ"(%struct.S* %s) #4
+ ret void
+}
+
+define internal void @"\01?test2@@YAX_N@Z.cleanup1"(i8*, i8*) #7 {
+entry:
+ %s1.i8 = call i8* @llvm.framerecover(i8* bitcast (void (i1)* @"\01?test2@@YAX_N@Z" to i8*), i8* %1, i32 1)
+ %s1 = bitcast i8* %s1.i8 to %struct.S*
+ call void @"\01??_DS@@QEAA@XZ"(%struct.S* %s1) #4
+ ret void
+}
+
+attributes #0 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-realign-stack" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" "wineh-parent"="\ 1?test1@@YAXXZ" }
+attributes #1 = { nounwind "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-realign-stack" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" }
+attributes #2 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-realign-stack" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" "wineh-parent"="\ 1?test2@@YAX_N@Z" }
+attributes #3 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-realign-stack" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" }
+attributes #4 = { nounwind }
+attributes #5 = { "wineh-parent"="\ 1?test1@@YAXXZ" }
+attributes #6 = { nounwind readnone }
+attributes #7 = { "wineh-parent"="\ 1?test2@@YAX_N@Z" }
+attributes #8 = { noreturn }