/// Indicate that this basic block is the entry block of an EH funclet.
bool IsEHFuncletEntry = false;
+ /// Indicate that this basic block is the entry block of a cleanup funclet.
+ bool IsCleanupFuncletEntry = false;
+
/// \brief since getSymbol is a relatively heavy-weight operation, the symbol
/// is only computed once and is cached.
mutable MCSymbol *CachedMCSymbol = nullptr;
/// Indicates if this is the entry block of an EH funclet.
void setIsEHFuncletEntry(bool V = true) { IsEHFuncletEntry = V; }
+ /// Returns true if this is the entry block of a cleanup funclet.
+ bool isCleanupFuncletEntry() const { return IsCleanupFuncletEntry; }
+
+ /// Indicates if this is the entry block of a cleanup funclet.
+ void setIsCleanupFuncletEntry(bool V = true) { IsCleanupFuncletEntry = V; }
+
// Code Layout methods.
/// Move 'this' block before or after the specified block. This only moves
// exceptions on Windows.
typedef PointerUnion<const BasicBlock *, MachineBasicBlock *> MBBOrBasicBlock;
-typedef PointerUnion<const Value *, MachineBasicBlock *> ValueOrMBB;
+typedef PointerUnion<const Value *, const MachineBasicBlock *> ValueOrMBB;
struct WinEHUnwindMapEntry {
int ToState;
/// MachineBasicBlock, an alignment (if present) and a comment describing
/// it if appropriate.
void AsmPrinter::EmitBasicBlockStart(const MachineBasicBlock &MBB) const {
+ // End the previous funclet and start a new one.
+ if (MBB.isEHFuncletEntry()) {
+ for (const HandlerInfo &HI : Handlers) {
+ HI.Handler->endFunclet();
+ HI.Handler->beginFunclet(MBB);
+ }
+ }
+
// Emit an alignment directive for this block, if needed.
if (unsigned Align = MBB.getAlignment())
EmitAlignment(Align);
namespace llvm {
+class MachineBasicBlock;
class MachineFunction;
class MachineInstr;
class MCSymbol;
/// beginFunction at all.
virtual void endFunction(const MachineFunction *MF) = 0;
+ /// \brief Emit target-specific EH funclet machinery.
+ virtual void beginFunclet(const MachineBasicBlock &MBB,
+ MCSymbol *Sym = nullptr) {}
+ virtual void endFunclet() {}
+
/// \brief Process beginning of an instruction.
virtual void beginInstruction(const MachineInstr *MI) = 0;
#include "llvm/MC/MCStreamer.h"
#include "llvm/MC/MCSymbol.h"
#include "llvm/MC/MCWin64EH.h"
+#include "llvm/Support/COFF.h"
#include "llvm/Support/Dwarf.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/FormattedStream.h"
return;
}
- if (shouldEmitMoves || shouldEmitPersonality)
- Asm->OutStreamer->EmitWinCFIStartProc(Asm->CurrentFnSym);
-
- if (shouldEmitPersonality) {
- const MCSymbol *PersHandlerSym =
- TLOF.getCFIPersonalitySymbol(Per, *Asm->Mang, Asm->TM, MMI);
- Asm->OutStreamer->EmitWinEHHandler(PersHandlerSym, true, true);
- }
+ beginFunclet(MF->front(), Asm->CurrentFnSym);
}
/// endFunction - Gather and emit post-function exception information.
if (!isMSVCEHPersonality(Per))
MMI->TidyLandingPads();
+ endFunclet();
+
if (shouldEmitPersonality || shouldEmitLSDA) {
Asm->OutStreamer->PushSection();
- if (shouldEmitMoves || shouldEmitPersonality) {
- // Emit an UNWIND_INFO struct describing the prologue.
- Asm->OutStreamer->EmitWinEHHandlerData();
- } else {
- // Just switch sections to the right xdata section. This use of
- // CurrentFnSym assumes that we only emit the LSDA when ending the parent
- // function.
- MCSection *XData = WinEH::UnwindEmitter::getXDataSection(
- Asm->CurrentFnSym, Asm->OutContext);
- Asm->OutStreamer->SwitchSection(XData);
- }
+ // Just switch sections to the right xdata section. This use of CurrentFnSym
+ // assumes that we only emit the LSDA when ending the parent function.
+ MCSection *XData = WinEH::UnwindEmitter::getXDataSection(Asm->CurrentFnSym,
+ Asm->OutContext);
+ Asm->OutStreamer->SwitchSection(XData);
// Emit the tables appropriate to the personality function in use. If we
// don't recognize the personality, assume it uses an Itanium-style LSDA.
Asm->OutStreamer->PopSection();
}
+}
+
+/// Retreive the MCSymbol for a GlobalValue or MachineBasicBlock. GlobalValues
+/// are used in the old WinEH scheme, and they will be removed eventually.
+static MCSymbol *getMCSymbolForMBBOrGV(AsmPrinter *Asm, ValueOrMBB Handler) {
+ if (!Handler)
+ return nullptr;
+ if (Handler.is<const MachineBasicBlock *>()) {
+ auto *MBB = Handler.get<const MachineBasicBlock *>();
+ assert(MBB->isEHFuncletEntry());
+
+ // Give catches and cleanups a name based off of their parent function and
+ // their funclet entry block's number.
+ const MachineFunction *MF = MBB->getParent();
+ const Function *F = MF->getFunction();
+ StringRef FuncLinkageName = GlobalValue::getRealLinkageName(F->getName());
+ MCContext &Ctx = MF->getContext();
+ StringRef HandlerPrefix = MBB->isCleanupFuncletEntry() ? "dtor" : "catch";
+ return Ctx.getOrCreateSymbol("?" + HandlerPrefix + "$" +
+ Twine(MBB->getNumber()) + "@?0?" +
+ FuncLinkageName + "@4HA");
+ }
+ return Asm->getSymbol(cast<GlobalValue>(Handler.get<const Value *>()));
+}
+
+void WinException::beginFunclet(const MachineBasicBlock &MBB,
+ MCSymbol *Sym) {
+ CurrentFuncletEntry = &MBB;
+
+ const Function *F = Asm->MF->getFunction();
+ // If a symbol was not provided for the funclet, invent one.
+ if (!Sym) {
+ Sym = getMCSymbolForMBBOrGV(Asm, &MBB);
+
+ // Describe our funclet symbol as a function with internal linkage.
+ Asm->OutStreamer->BeginCOFFSymbolDef(Sym);
+ Asm->OutStreamer->EmitCOFFSymbolStorageClass(COFF::IMAGE_SYM_CLASS_STATIC);
+ Asm->OutStreamer->EmitCOFFSymbolType(COFF::IMAGE_SYM_DTYPE_FUNCTION
+ << COFF::SCT_COMPLEX_TYPE_SHIFT);
+ Asm->OutStreamer->EndCOFFSymbolDef();
+
+ // We want our funclet's entry point to be aligned such that no nops will be
+ // present after the label.
+ Asm->EmitAlignment(std::max(Asm->MF->getAlignment(), MBB.getAlignment()),
+ F);
+
+ // Now that we've emitted the alignment directive, point at our funclet.
+ Asm->OutStreamer->EmitLabel(Sym);
+ }
+ // Mark 'Sym' as starting our funclet.
if (shouldEmitMoves || shouldEmitPersonality)
+ Asm->OutStreamer->EmitWinCFIStartProc(Sym);
+
+ if (shouldEmitPersonality) {
+ const TargetLoweringObjectFile &TLOF = Asm->getObjFileLowering();
+ const Function *PerFn = nullptr;
+
+ // Determine which personality routine we are using for this funclet.
+ if (F->hasPersonalityFn())
+ PerFn = dyn_cast<Function>(F->getPersonalityFn()->stripPointerCasts());
+ const MCSymbol *PersHandlerSym =
+ TLOF.getCFIPersonalitySymbol(PerFn, *Asm->Mang, Asm->TM, MMI);
+
+ // Classify the personality routine so that we may reason about it.
+ EHPersonality Per = EHPersonality::Unknown;
+ if (F->hasPersonalityFn())
+ Per = classifyEHPersonality(F->getPersonalityFn());
+
+ // Do not emit a .seh_handler directive if it is a C++ cleanup funclet.
+ if (Per != EHPersonality::MSVC_CXX ||
+ !CurrentFuncletEntry->isCleanupFuncletEntry())
+ Asm->OutStreamer->EmitWinEHHandler(PersHandlerSym, true, true);
+ }
+}
+
+void WinException::endFunclet() {
+ // No funclet to process? Great, we have nothing to do.
+ if (!CurrentFuncletEntry)
+ return;
+
+ if (shouldEmitMoves || shouldEmitPersonality) {
+ const Function *F = Asm->MF->getFunction();
+ EHPersonality Per = EHPersonality::Unknown;
+ if (F->hasPersonalityFn())
+ Per = classifyEHPersonality(F->getPersonalityFn());
+
+ // The .seh_handlerdata directive implicitly switches section, push the
+ // current section so that we may return to it.
+ Asm->OutStreamer->PushSection();
+
+ // 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()) {
+ StringRef FuncLinkageName = GlobalValue::getRealLinkageName(F->getName());
+ MCSymbol *FuncInfoXData = Asm->OutContext.getOrCreateSymbol(
+ Twine("$cppxdata$", FuncLinkageName));
+ Asm->OutStreamer->EmitValue(create32bitRef(FuncInfoXData), 4);
+ }
+
+ // Switch back to the previous section now that we are done writing to
+ // .xdata.
+ Asm->OutStreamer->PopSection();
+
+ // Emit a .seh_endproc directive to mark the end of the function.
Asm->OutStreamer->EmitWinCFIEndProc();
+ }
+
+ // Let's make sure we don't try to end the same funclet twice.
+ CurrentFuncletEntry = nullptr;
}
const MCExpr *WinException::create32bitRef(const MCSymbol *Value) {
}
}
-/// Retreive the MCSymbol for a GlobalValue or MachineBasicBlock. GlobalValues
-/// are used in the old WinEH scheme, and they will be removed eventually.
-static MCSymbol *getMCSymbolForMBBOrGV(AsmPrinter *Asm, ValueOrMBB Handler) {
- if (!Handler)
- return nullptr;
- if (Handler.is<MachineBasicBlock *>())
- return Handler.get<MachineBasicBlock *>()->getSymbol();
- return Asm->getSymbol(cast<GlobalValue>(Handler.get<const Value *>()));
-}
-
void WinException::emitCXXFrameHandler3Table(const MachineFunction *MF) {
const Function *F = MF->getFunction();
auto &OS = *Asm->OutStreamer;
// IPs to state numbers.
FuncInfoXData =
Asm->OutContext.getOrCreateSymbol(Twine("$cppxdata$", FuncLinkageName));
- OS.EmitValue(create32bitRef(FuncInfoXData), 4);
computeIP2StateTable(MF, FuncInfo, IPToStateTable);
} else {
FuncInfoXData = Asm->OutContext.getOrCreateLSDASymbol(FuncLinkageName);
/// True if this is a 64-bit target and we should use image relative offsets.
bool useImageRel32 = false;
+ /// Pointer to the current funclet entry BB.
+ const MachineBasicBlock *CurrentFuncletEntry = nullptr;
+
void emitCSpecificHandlerTable(const MachineFunction *MF);
/// Emit the EH table data for 32-bit and 64-bit functions using
/// Gather and emit post-function exception information.
void endFunction(const MachineFunction *) override;
+
+ /// \brief Emit target-specific EH funclet machinery.
+ void beginFunclet(const MachineBasicBlock &MBB, MCSymbol *Sym) override;
+ void endFunclet() override;
};
}
// Don't emit any special code for the cleanuppad instruction. It just marks
// the start of a funclet.
FuncInfo.MBB->setIsEHFuncletEntry();
+ FuncInfo.MBB->setIsCleanupFuncletEntry();
}
/// When an invoke or a cleanupret unwinds to the next EH pad, there are
// Reset EBP / ESI to something good.
MBBI = restoreWin32EHStackPointers(MBB, MBBI, DL);
} else {
- // FIXME: Add SEH directives.
- NeedsWinCFI = false;
// Immediately spill RDX into the home slot. The runtime cares about this.
unsigned RDX = Uses64BitFramePtr ? X86::RDX : X86::EDX;
// MOV64mr %rdx, 16(%rsp)
BuildMI(MBB, MBBI, DL, TII.get(X86::PUSH64r))
.addReg(MachineFramePtr, RegState::Kill)
.setMIFlag(MachineInstr::FrameSetup);
+ BuildMI(MBB, MBBI, DL, TII.get(X86::SEH_PushReg))
+ .addImm(MachineFramePtr)
+ .setMIFlag(MachineInstr::FrameSetup);
// MOV64rr %rdx, %rbp
unsigned MOVrr = Uses64BitFramePtr ? X86::MOV64rr : X86::MOV32rr;
BuildMI(MBB, MBBI, DL, TII.get(MOVrr), FramePtr)
; CHECK: .seh_handler __C_specific_handler
; CHECK-NOT: jmpq *
; CHECK: .seh_handlerdata
+; CHECK-NEXT: .text
+; CHECK: .seh_endproc
+; CHECK: .section .xdata,"dr"
; CHECK-NEXT: .long 1
; CHECK-NEXT: .long .Ltmp{{.*}}
; CHECK-NEXT: .long .Ltmp{{.*}}
; MINGW64: .seh_setframe 5, 32
; MINGW64: callq _Unwind_Resume
; MINGW64: .seh_handlerdata
+; MINGW64: .seh_endproc
; MINGW64: GCC_except_table0:
; MINGW64: Lexception0:
-; MINGW64: .seh_endproc
; MINGW32: .cfi_startproc
; MINGW32: .cfi_personality 0, ___gxx_personality_v0
; CHECK: callq printf
; CHECK: .seh_handlerdata
+; CHECK-NEXT: .text
+; CHECK-NEXT: .Ltmp{{[0-9]+}}
+; CHECK-NEXT: .seh_endproc
+; CHECK-NEXT: .section .xdata,"dr"
; CHECK-NEXT: .long 1
; CHECK-NEXT: .long .Ltmp{{[0-9]+}}@IMGREL
; CHECK-NEXT: .long .Ltmp{{[0-9]+}}@IMGREL+1
; CHECK: retq
;
; CHECK: .seh_handlerdata
+; CHECK-NEXT: .text
+; CHECK-NEXT: .Ltmp{{[0-9]+}}
+; CHECK-NEXT: .seh_endproc
+; CHECK-NEXT: .section .xdata,"dr"
; CHECK-NEXT: .long 3
; CHECK-NEXT: .long .Ltmp0@IMGREL
; CHECK-NEXT: .long .Ltmp1@IMGREL+1
; X64: retq
; X64: .seh_handlerdata
+; X64-NEXT: .text
+; X64-NEXT: .Ltmp{{[0-9]+}}:
+; X64-NEXT: .seh_endproc
+; X64-NEXT: .section .xdata,"dr"
; X64-NEXT: .long 1
; X64-NEXT: .long .Ltmp0@IMGREL
; X64-NEXT: .long .Ltmp1@IMGREL
; CHECK: jmp [[cont_bb]]
; CHECK: .seh_handlerdata
+; CHECK-NEXT: .text
+; CHECK-NEXT: .Ltmp{{[0-9]+}}
+; CHECK-NEXT: .seh_endproc
+; CHECK-NEXT: .section .xdata,"dr"
; CHECK-NEXT: .long 2
; CHECK-NEXT: .long .Ltmp0@IMGREL
; CHECK-NEXT: .long .Ltmp1@IMGREL+1
; X86: addl $12, %ebp
; X86: jmp [[contbb]]
-; X86: [[catch1bb:LBB0_[0-9]+]]: # %catch{{$}}
+; X86: "?catch$[[catch1bb:[0-9]+]]@?0?try_catch_catch@4HA":
+; X86: LBB0_[[catch1bb]]: # %catch{{$}}
; X86: pushl %ebp
; X86-NOT: pushl
; X86: addl $12, %ebp
; X86: .long 0
; X86: .long "??_R0H@8"
; X86: .long 0
-; X86: .long [[catch1bb]]
+; X86: .long "?catch$[[catch1bb]]@?0?try_catch_catch@4HA"
; X64-LABEL: try_catch_catch:
; X64: pushq %rbp
; X64: popq %rbp
; X64: retq
-; X64: [[catch1bb:\.LBB0_[0-9]+]]: # %catch{{$}}
+; X64: "?catch$[[catch1bb:[0-9]+]]@?0?try_catch_catch@4HA":
+; X64: LBB0_[[catch1bb]]: # %catch{{$}}
; X64: movq %rdx, 16(%rsp)
; X64: pushq %rbp
; X64: movq %rdx, %rbp
; X64: .long 0
; X64: .long "??_R0H@8"@IMGREL
; X64: .long 0
-; X64: .long [[catch1bb]]@IMGREL
+; X64: .long "?catch$[[catch1bb]]@?0?try_catch_catch@4HA"@IMGREL
; X64: .long 56
; X86: addl $12, %ebp
; X86: jmp [[contbb]]
-; X86: [[catch1bb:LBB0_[0-9]+]]: # %catch{{$}}
+; X86: "?catch$[[catch1bb:[0-9]+]]@?0?try_catch_catch@4HA":
+; X86: LBB0_[[catch1bb]]: # %catch{{$}}
; X86: pushl %ebp
; X86: addl $12, %ebp
; X86: subl $8, %esp
; X86-NEXT: movl $[[restorebb]], %eax
; X86-NEXT: retl
-; X86: [[catch2bb:LBB0_[0-9]+]]: # %catch.2{{$}}
+; X86: "?catch$[[catch2bb:[0-9]+]]@?0?try_catch_catch@4HA":
+; X86: LBB0_[[catch2bb]]: # %catch.2{{$}}
; X86: pushl %ebp
; X86: addl $12, %ebp
; X86: subl $8, %esp
; X86-NEXT: .long 0
; X86-NEXT: .long "??_R0H@8"
; X86-NEXT: .long -20
-; X86-NEXT: .long [[catch1bb]]
+; X86-NEXT: .long "?catch$[[catch1bb]]@?0?try_catch_catch@4HA"
; X86-NEXT: .long 64
; X86-NEXT: .long 0
; X86-NEXT: .long 0
-; X86-NEXT: .long [[catch2bb]]
+; X86-NEXT: .long "?catch$[[catch2bb]]@?0?try_catch_catch@4HA"
; X64-LABEL: try_catch_catch:
; X64: Lfunc_begin0:
; X64: popq %rbp
; X64: retq
-; X64: [[catch1bb:\.LBB0_[0-9]+]]: # %catch{{$}}
+; X64: "?catch$[[catch1bb:[0-9]+]]@?0?try_catch_catch@4HA":
+; X64: LBB0_[[catch1bb]]: # %catch{{$}}
; X64: movq %rdx, 16(%rsp)
; X64: pushq %rbp
; X64: movq %rdx, %rbp
; X64-NEXT: leaq [[contbb]](%rip), %rax
; X64-NEXT: retq
-; X64: [[catch2bb:\.LBB0_[0-9]+]]: # %catch.2{{$}}
+; X64: "?catch$[[catch2bb:[0-9]+]]@?0?try_catch_catch@4HA":
+; X64: LBB0_[[catch2bb]]: # %catch.2{{$}}
; X64: movq %rdx, 16(%rsp)
; X64: pushq %rbp
; X64: movq %rdx, %rbp
; X64-NEXT: .long "??_R0H@8"@IMGREL
; FIXME: This should probably be offset from rsp, not rbp.
; X64-NEXT: .long [[e_addr]]
-; X64-NEXT: .long [[catch1bb]]@IMGREL
+; X64-NEXT: .long "?catch$[[catch1bb]]@?0?try_catch_catch@4HA"@IMGREL
; X64-NEXT: .long 56
; X64-NEXT: .long 64
; X64-NEXT: .long 0
; X64-NEXT: .long 0
-; X64-NEXT: .long [[catch2bb]]@IMGREL
+; X64-NEXT: .long "?catch$[[catch2bb]]@?0?try_catch_catch@4HA"@IMGREL
; X64-NEXT: .long 56
; X64: $ip2state$try_catch_catch:
; X86: movl $3, (%esp)
; X86: calll _f
-; X86: LBB1_[[cleanup_inner:[0-9]+]]: # %cleanup.inner
+; X86: "?dtor$[[cleanup_inner:[0-9]+]]@?0?nested_cleanup@4HA":
+; X86: LBB1_[[cleanup_inner]]: # %cleanup.inner{{$}}
; X86: pushl %ebp
; X86: leal {{.*}}(%ebp), %ecx
; X86: calll "??1Dtor@@QAE@XZ"
; X86: popl %ebp
; X86: retl
-; X86: LBB1_[[cleanup_outer:[0-9]+]]: # %cleanup.outer
+; X86: "?dtor$[[cleanup_outer:[0-9]+]]@?0?nested_cleanup@4HA":
+; X86: LBB1_[[cleanup_outer]]: # %cleanup.outer{{$}}
; X86: pushl %ebp
; X86: leal {{.*}}(%ebp), %ecx
; X86: calll "??1Dtor@@QAE@XZ"
; X86: .long 1
; X86: $stateUnwindMap$nested_cleanup:
; X86: .long -1
-; X86: .long LBB1_[[cleanup_outer]]
+; X86: .long "?dtor$[[cleanup_outer]]@?0?nested_cleanup@4HA"
; X86: .long 0
-; X86: .long LBB1_[[cleanup_inner]]
+; X86: .long "?dtor$[[cleanup_inner]]@?0?nested_cleanup@4HA"
; X64-LABEL: nested_cleanup:
; X64: .Lfunc_begin1:
-; X64: .Ltmp8:
+; X64: .Ltmp14:
; X64: movl $1, %ecx
; X64: callq f
-; X64: .Ltmp10:
+; X64: .Ltmp16:
; X64: movl $2, %ecx
; X64: callq f
-; X64: .Ltmp11:
+; X64: .Ltmp17:
; X64: callq "??1Dtor@@QAE@XZ"
-; X64: .Ltmp12:
+; X64: .Ltmp18:
; X64: movl $3, %ecx
; X64: callq f
-; X64: .Ltmp13:
+; X64: .Ltmp19:
-; X64: .LBB1_[[cleanup_inner:[0-9]+]]: # %cleanup.inner
+; X64: "?dtor$[[cleanup_inner:[0-9]+]]@?0?nested_cleanup@4HA":
+; X64: LBB1_[[cleanup_inner]]: # %cleanup.inner{{$}}
; X64: pushq %rbp
; X64: leaq {{.*}}(%rbp), %rcx
; X64: callq "??1Dtor@@QAE@XZ"
; X64: popq %rbp
; X64: retq
-; X64: .LBB1_[[cleanup_outer:[0-9]+]]: # %cleanup.outer
+; X64: .seh_handlerdata
+; X64: .text
+; X64: .seh_endproc
+
+; X64: "?dtor$[[cleanup_outer:[0-9]+]]@?0?nested_cleanup@4HA":
+; X64: LBB1_[[cleanup_outer]]: # %cleanup.outer{{$}}
; X64: pushq %rbp
; X64: leaq {{.*}}(%rbp), %rcx
; X64: callq "??1Dtor@@QAE@XZ"
; X64: popq %rbp
; X64: retq
-; X64: .seh_handlerdata
-; X64-NEXT: .long ($cppxdata$nested_cleanup)@IMGREL
+; X64: .section .xdata,"dr"
; X64-NEXT: .align 4
; X64: $cppxdata$nested_cleanup:
; X64-NEXT: .long 429065506
; X64: $stateUnwindMap$nested_cleanup:
; X64-NEXT: .long -1
-; X64-NEXT: .long .LBB1_[[cleanup_outer]]@IMGREL
+; X64-NEXT: .long "?dtor$[[cleanup_outer]]@?0?nested_cleanup@4HA"@IMGREL
; X64-NEXT: .long 0
-; X64-NEXT: .long .LBB1_[[cleanup_inner]]@IMGREL
+; X64-NEXT: .long "?dtor$[[cleanup_inner]]@?0?nested_cleanup@4HA"@IMGREL
; X64: $ip2state$nested_cleanup:
; X64-NEXT: .long .Lfunc_begin1@IMGREL
; X64-NEXT: .long -1
-; X64-NEXT: .long .Ltmp8@IMGREL
+; X64-NEXT: .long .Ltmp14@IMGREL
; X64-NEXT: .long 0
-; X64-NEXT: .long .Ltmp10@IMGREL
+; X64-NEXT: .long .Ltmp16@IMGREL
; X64-NEXT: .long 1
-; X64-NEXT: .long .Ltmp12@IMGREL
+; X64-NEXT: .long .Ltmp18@IMGREL
; X64-NEXT: .long 0
-; X64-NEXT: .long .Ltmp13@IMGREL+1
+; X64-NEXT: .long .Ltmp19@IMGREL+1
; X64-NEXT: .long -1
attributes #0 = { "disable-tail-calls"="false" "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" }
--- /dev/null
+; RUN: llc -mtriple=x86_64-windows-msvc < %s | FileCheck %s
+
+target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-pc-windows-msvc"
+
+define void @"\01?f@@YAXXZ"(i1 %B) personality i32 (...)* @__CxxFrameHandler3 {
+entry:
+ invoke void @g()
+ to label %unreachable unwind label %cleanupblock
+
+cleanupblock:
+ %cleanp = cleanuppad []
+ call void @g()
+ cleanupret %cleanp unwind label %catch.dispatch
+
+catch.dispatch:
+ %cp = catchpad [i8* null, i32 64, i8* null]
+ to label %catch unwind label %catchendblock
+
+catch:
+ call void @g()
+ catchret %cp to label %try.cont
+
+try.cont:
+ ret void
+
+catchendblock:
+ catchendpad unwind to caller
+
+unreachable:
+ unreachable
+}
+
+
+declare void @g()
+
+declare i32 @__CxxFrameHandler3(...)
+
+; Destructors need CFI but they shouldn't use the .seh_handler directive.
+; CHECK: "?dtor$[[cleanup:[0-9]+]]@?0??f@@YAXXZ@4HA":
+; CHECK: .seh_proc "?dtor$[[cleanup]]@?0??f@@YAXXZ@4HA"
+; CHECK-NOT: .seh_handler __CxxFrameHandler3
+; CHECK: LBB0_[[cleanup]]: # %cleanupblock{{$}}
+
+; Emit CFI for pushing RBP.
+; CHECK: movq %rdx, 16(%rsp)
+; CHECK: pushq %rbp
+; CHECK: .seh_pushreg 5
+
+; Emit CFI for allocating from the stack pointer.
+; CHECK: subq $32, %rsp
+; CHECK: .seh_stackalloc 32
+
+; FIXME: This looks wrong...
+; CHECK: leaq 32(%rsp), %rbp
+; CHECK: .seh_setframe 5, 32
+
+; Prologue is done, emit the .seh_endprologue directive.
+; CHECK: .seh_endprologue
+
+; Make sure there is a nop after a call if the call precedes the epilogue.
+; CHECK: callq g
+; CHECK-NEXT: nop
+
+; Don't emit a reference to the LSDA.
+; CHECK: .seh_handlerdata
+; CHECK-NOT: .long ("$cppxdata$?f@@YAXXZ")@IMGREL
+; CHECK-NEXT: .text
+; CHECK: .seh_endproc
+
+; CHECK: "?catch$[[catch:[0-9]+]]@?0??f@@YAXXZ@4HA":
+; CHECK: .seh_proc "?catch$[[catch]]@?0??f@@YAXXZ@4HA"
+; CHECK-NEXT: .seh_handler __CxxFrameHandler3, @unwind, @except
+; CHECK: LBB0_[[catch]]: # %catch{{$}}
+
+; Emit CFI for pushing RBP.
+; CHECK: movq %rdx, 16(%rsp)
+; CHECK: pushq %rbp
+; CHECK: .seh_pushreg 5
+
+; Emit CFI for allocating from the stack pointer.
+; CHECK: subq $32, %rsp
+; CHECK: .seh_stackalloc 32
+
+; FIXME: This looks wrong...
+; CHECK: leaq 32(%rsp), %rbp
+; CHECK: .seh_setframe 5, 32
+
+; Prologue is done, emit the .seh_endprologue directive.
+; CHECK: .seh_endprologue
+
+; Make sure there is a nop after a call if the call precedes the epilogue.
+; CHECK: callq g
+; CHECK-NEXT: nop
+
+; Emit a reference to the LSDA.
+; CHECK: .seh_handlerdata
+; CHECK-NEXT: .long ("$cppxdata$?f@@YAXXZ")@IMGREL
+; CHECK-NEXT: .text
+; CHECK: .seh_endproc