Index: lib/CodeGen/AsmPrinter/Win64Exception.cpp =================================================================== --- lib/CodeGen/AsmPrinter/Win64Exception.cpp +++ lib/CodeGen/AsmPrinter/Win64Exception.cpp @@ -78,9 +78,9 @@ if (!shouldEmitPersonality) return; - MCSymbol *GCCHandlerSym = - Asm->GetExternalSymbolSymbol("_GCC_specific_handler"); - Asm->OutStreamer.EmitWin64EHHandler(GCCHandlerSym, true, true); + const MCSymbol *PersHandlerSym = TLOF.getCFIPersonalitySymbol(Per, *Asm->Mang, + Asm->TM, MMI); + Asm->OutStreamer.EmitWin64EHHandler(PersHandlerSym, true, true); Asm->OutStreamer.EmitLabel(Asm->GetTempSymbol("eh_func_begin", Asm->getFunctionNumber())); @@ -99,15 +99,8 @@ MMI->TidyLandingPads(); if (shouldEmitPersonality) { - const TargetLoweringObjectFile &TLOF = Asm->getObjFileLowering(); - const Function *Per = MMI->getPersonalities()[MMI->getPersonalityIndex()]; - const MCSymbol *Sym = - TLOF.getCFIPersonalitySymbol(Per, *Asm->Mang, Asm->TM, MMI); - Asm->OutStreamer.PushSection(); Asm->OutStreamer.EmitWin64EHHandlerData(); - Asm->OutStreamer.EmitValue(MCSymbolRefExpr::Create(Sym, Asm->OutContext), - 4); EmitExceptionTable(); Asm->OutStreamer.PopSection(); } Index: lib/MC/MCObjectFileInfo.cpp =================================================================== --- lib/MC/MCObjectFileInfo.cpp +++ lib/MC/MCObjectFileInfo.cpp @@ -606,11 +606,16 @@ // though it contains relocatable pointers. In PIC mode, this is probably a // big runtime hit for C++ apps. Either the contents of the LSDA need to be // adjusted or this should be a data section. - LSDASection = - Ctx->getCOFFSection(".gcc_except_table", - COFF::IMAGE_SCN_CNT_INITIALIZED_DATA | - COFF::IMAGE_SCN_MEM_READ, - SectionKind::getReadOnly()); + if (T.getOS() == Triple::Win32) { + // On Windows with SEH, the LSDA is emitted into the .xdata section + LSDASection = 0; + } else { + LSDASection = + Ctx->getCOFFSection(".gcc_except_table", + COFF::IMAGE_SCN_CNT_INITIALIZED_DATA | + COFF::IMAGE_SCN_MEM_READ, + SectionKind::getReadOnly()); + } // Debug info. COFFDebugSymbolsSection = Index: lib/Target/X86/MCTargetDesc/X86MCAsmInfo.cpp =================================================================== --- lib/Target/X86/MCTargetDesc/X86MCAsmInfo.cpp +++ lib/Target/X86/MCTargetDesc/X86MCAsmInfo.cpp @@ -141,9 +141,15 @@ void X86MCAsmInfoMicrosoft::anchor() { } -X86MCAsmInfoMicrosoft::X86MCAsmInfoMicrosoft(const Triple &Triple) { - if (Triple.getArch() == Triple::x86_64) +X86MCAsmInfoMicrosoft::X86MCAsmInfoMicrosoft(const Triple &T) { + bool is64Bit = T.getArch() == Triple::x86_64; + + if (is64Bit) { PrivateGlobalPrefix = ".L"; + ExceptionsType = ExceptionHandling::Win64; + } + + PointerSize = is64Bit ? 8 : 4; AssemblerDialect = AsmWriterFlavor; @@ -156,18 +162,23 @@ void X86MCAsmInfoGNUCOFF::anchor() { } -X86MCAsmInfoGNUCOFF::X86MCAsmInfoGNUCOFF(const Triple &Triple) { - if (Triple.getArch() == Triple::x86_64) { +X86MCAsmInfoGNUCOFF::X86MCAsmInfoGNUCOFF(const Triple &T) { + bool is64Bit = T.getArch() == Triple::x86_64; + + if (is64Bit) PrivateGlobalPrefix = ".L"; - PointerSize = 8; - } + + PointerSize = is64Bit ? 8 : 4; AssemblerDialect = AsmWriterFlavor; TextAlignFillValue = 0x90; // Exceptions handling - ExceptionsType = ExceptionHandling::DwarfCFI; + if (is64Bit && T.isWindowsGNUEnvironment()) + ExceptionsType = ExceptionHandling::Win64; + else + ExceptionsType = ExceptionHandling::DwarfCFI; UseIntegratedAssembler = true; } Index: lib/Target/X86/X86AsmPrinter.h =================================================================== --- lib/Target/X86/X86AsmPrinter.h +++ lib/Target/X86/X86AsmPrinter.h @@ -27,6 +27,8 @@ const X86Subtarget *Subtarget; StackMaps SM; + void EmitUnwindingInstruction(const MachineInstr *MI); + public: explicit X86AsmPrinter(TargetMachine &TM, MCStreamer &Streamer) : AsmPrinter(TM, Streamer), SM(*this) { Index: lib/Target/X86/X86ISelLowering.cpp =================================================================== --- lib/Target/X86/X86ISelLowering.cpp +++ lib/Target/X86/X86ISelLowering.cpp @@ -597,7 +597,8 @@ // FIXME - use subtarget debug flags if (!Subtarget->isTargetDarwin() && !Subtarget->isTargetELF() && - !Subtarget->isTargetCygMing()) { + !Subtarget->isTargetCygMing() && + !Subtarget->isTargetWin64()) { setOperationAction(ISD::EH_LABEL, MVT::Other, Expand); } Index: lib/Target/X86/X86MCInstLower.cpp =================================================================== --- lib/Target/X86/X86MCInstLower.cpp +++ lib/Target/X86/X86MCInstLower.cpp @@ -13,6 +13,7 @@ //===----------------------------------------------------------------------===// #include "X86AsmPrinter.h" +#include "X86RegisterInfo.h" #include "InstPrinter/X86ATTInstPrinter.h" #include "MCTargetDesc/X86BaseInfo.h" #include "llvm/ADT/SmallString.h" @@ -20,6 +21,7 @@ #include "llvm/CodeGen/MachineModuleInfoImpls.h" #include "llvm/CodeGen/StackMaps.h" #include "llvm/IR/DataLayout.h" +#include "llvm/IR/Function.h" #include "llvm/IR/GlobalValue.h" #include "llvm/IR/Mangler.h" #include "llvm/MC/MCAsmInfo.h" @@ -777,7 +779,129 @@ EmitNops(OS, NumBytes - EncodedBytes, Is64Bit, STI); } +static bool isCalleeSavedReg(const MachineFunction *MF, + const X86RegisterInfo *RI, unsigned Reg) { + const uint16_t *NVRegs = RI->getCalleeSavedRegs(MF); + while (*NVRegs) { + if (*NVRegs == Reg) + return true; + NVRegs++; + } + return false; +} + +void X86AsmPrinter::EmitUnwindingInstruction(const MachineInstr *MI) { + assert(MI->getFlag(MachineInstr::FrameSetup) && + "Only call frame setup instructions allowed here!"); + unsigned SReg, DReg; + int64_t Offset; + const X86RegisterInfo *RI = + static_cast(TM.getRegisterInfo()); + switch (MI->getOpcode()) { + default: llvm_unreachable("Unknown frame setup opcode!"); + case X86::PUSH64r: + SReg = MI->getOperand(0).getReg(); + if (isCalleeSavedReg(MI->getParent()->getParent(), RI, SReg)) + OutStreamer.EmitWin64EHPushReg(RI->getSEHRegNum(SReg)); + else + OutStreamer.EmitWin64EHAllocStack(8); + break; + case X86::SUB64ri8: + case X86::SUB64ri32: + DReg = MI->getOperand(0).getReg(); + Offset = MI->getOperand(2).getImm(); + if (DReg == RI->getStackRegister()) { + OutStreamer.EmitWin64EHAllocStack(Offset); + } else + llvm_unreachable("X86::SUB64ri8/32 not allowed with these operands"); + break; + case X86::MOV64rr: + DReg = MI->getOperand(0).getReg(); + SReg = MI->getOperand(1).getReg(); + if (DReg == RI->getFrameRegister(*MF) && SReg == RI->getStackRegister()) + OutStreamer.EmitWin64EHSetFrame(RI->getSEHRegNum(DReg), 0); + else + llvm_unreachable("X86::MOV64rr not allowed with these operands"); + break; + case X86::MOV64mr: + case X86::MOVAPSmr: + DReg = MI->getOperand(0).getReg(); + SReg = MI->getOperand(5).getReg(); + if (DReg == RI->getFrameRegister(*MF) || DReg == RI->getStackRegister()) { + Offset = MI->getOperand(3).getImm(); + if (MI->getOperand(1).getImm() != 1 || MI->getOperand(2).getImm() != 0 || + MI->getOperand(4).getImm() != 0) + llvm_unreachable("X86::MOV64mr not allowed with these operands"); + if (MI->getOpcode() == X86::MOVAPSmr) + OutStreamer.EmitWin64EHSaveXMM(RI->getSEHRegNum(SReg), Offset); + else + OutStreamer.EmitWin64EHSaveReg(RI->getSEHRegNum(SReg), Offset); + } else + llvm_unreachable("X86::MOV64mr not allowed with these operands"); + break; + case X86::LEA64r: + DReg = MI->getOperand(0).getReg(); + SReg = MI->getOperand(1).getReg(); + Offset = MI->getOperand(4).getImm(); + if (SReg == RI->getStackRegister() && Offset > 0) { + // Check stack register first in case there is no frame register. + if (DReg == RI->getStackRegister()) { + OutStreamer.EmitWin64EHAllocStack(Offset); + } else if (DReg == RI->getFrameRegister(*MF)) { + OutStreamer.EmitWin64EHSetFrame(RI->getSEHRegNum(DReg), Offset); + } else + llvm_unreachable("X86::LEA64r not allowed with these operands"); + } + else + llvm_unreachable("X86::LEA64r not allowed with these operands"); + break; + case X86::MOV64ri: + // Occurs before alloca()/call to __chkstk/___chkstk_ms + if (!(MI->getNextNode() + && MI->getNextNode()->getOpcode() == X86::W64ALLOCA + && MI->getOperand(0).getReg() == X86::RAX)) + llvm_unreachable("X86::MOV64ri only allowed before X86::W64ALLOCA"); + break; + case X86::W64ALLOCA: + // Occurs if call to __chkstk/___chkstk_ms is used. + break; + case X86::SUB64rr: + // Occurs after call to __chkstk/___chkstk_ms on Win64 + if (!(MI->getPrevNode() + && MI->getPrevNode()->getOpcode() == X86::W64ALLOCA + && MI->getOperand(0).getReg() == RI->getStackRegister() + && MI->getOperand(2).getReg() == X86::RAX + && getSubtarget().isTargetWin64())) + llvm_unreachable("X86::SUB64rr only allowed after X86::W64ALLOCA"); + const MachineInstr *MoveMI = MI->getPrevNode()->getPrevNode(); + if (!(MoveMI && MoveMI->getOpcode() == X86::MOV64ri)) + llvm_unreachable("X86::MOV64ri required before X86::W64ALLOCA"); + Offset = MoveMI->getOperand(1).getImm(); + OutStreamer.EmitWin64EHAllocStack(Offset); + break; + } + // Prolog ends if next instruction does not have the FrameSetup flag + // and is not the CFI_Instruction. + if (MI->getNextNode()) { + const MachineInstr *MI2 = MI->getNextNode(); + if (!(MI2->getFlag(MachineInstr::FrameSetup) || + (MI2->isCFIInstruction() && MI2->getNextNode() && + MI2->getNextNode()->getFlag(MachineInstr::FrameSetup)))) { + OutStreamer.EmitWin64EHEndProlog(); + } + } +} + void X86AsmPrinter::EmitInstruction(const MachineInstr *MI) { + // Emit end of prolog for Win64-style EH if there is no framesetup. + if (MAI->getExceptionHandlingType() == ExceptionHandling::Win64 && + !MI->getFlag(MachineInstr::FrameSetup) && + MI->getParent()->getParent()->getFunction()->needsUnwindTableEntry() && + MI == MI->getParent()->instr_begin() && + MI->getParent()->getNumber() == 0) { + OutStreamer.EmitWin64EHEndProlog(); + } + X86MCInstLower MCInstLowering(*MF, *this); switch (MI->getOpcode()) { case TargetOpcode::DBG_VALUE: @@ -888,4 +1012,11 @@ MCInst TmpInst; MCInstLowering.Lower(MI, TmpInst); EmitToStreamer(OutStreamer, TmpInst); + + // Emit SEH unwind info for Win64-style EH. + if (MAI->getExceptionHandlingType() == ExceptionHandling::Win64 && + MI->getFlag(MachineInstr::FrameSetup) && + MI->getParent()->getParent()->getFunction()->needsUnwindTableEntry()) { + EmitUnwindingInstruction(MI); + } } Index: test/CodeGen/X86/avx-win64-args.ll =================================================================== --- test/CodeGen/X86/avx-win64-args.ll +++ test/CodeGen/X86/avx-win64-args.ll @@ -1,4 +1,6 @@ ; RUN: llc < %s -mcpu=corei7-avx -mattr=+avx | FileCheck %s +; XFAIL: * +; See PR16779. target triple = "x86_64-pc-win32" declare <8 x float> @foo(<8 x float>, i32) Index: test/CodeGen/X86/win64_eh.ll =================================================================== --- /dev/null +++ test/CodeGen/X86/win64_eh.ll @@ -0,0 +1,130 @@ +; RUN: llc < %s -O0 -mcpu=corei7 -mtriple=x86_64-pc-win32 | FileCheck %s -check-prefix=WIN64 +; RUN: llc < %s -O0 -mcpu=corei7 -mtriple=x86_64-pc-mingw32 | FileCheck %s -check-prefix=WIN64 + +; Check function without prolog +define void @foo0() uwtable { +entry: + ret void +} +; WIN64: .seh_proc foo0 +; WIN64: .seh_endprologue +; WIN64: ret +; WIN64: .seh_endproc + +; Checks a small stack allocation +define void @foo1() uwtable { +entry: + %baz = alloca [2000 x i16], align 2 + ret void +} +; WIN64: .seh_proc foo1 +; WIN64: subq $4000, %rsp +; WIN64: .seh_stackalloc 4000 +; WIN64: .seh_endprologue +; WIN64: addq $4000, %rsp +; WIN64: ret +; WIN64: .seh_endproc + +; Checks a stack allocation requiring call to __chkstk/___chkstk_ms +define void @foo2() uwtable { +entry: + %baz = alloca [4000 x i16], align 2 + ret void +} +; WIN64: .seh_proc foo2 +; WIN64: movabsq $8000, %rax +; WIN64: callq {{__chkstk|___chkstk_ms}} +; WIN64: subq %rax, %rsp +; WIN64: .seh_stackalloc 8000 +; WIN64: .seh_endprologue +; WIN64: addq $8000, %rsp +; WIN64: ret +; WIN64: .seh_endproc + + +; Checks stack push +define i32 @foo3(i32 %f_arg, i32 %e_arg, i32 %d_arg, i32 %c_arg, i32 %b_arg, i32 %a_arg) uwtable { +entry: + %a = alloca i32 + %b = alloca i32 + %c = alloca i32 + %d = alloca i32 + %e = alloca i32 + %f = alloca i32 + store i32 %a_arg, i32* %a + store i32 %b_arg, i32* %b + store i32 %c_arg, i32* %c + store i32 %d_arg, i32* %d + store i32 %e_arg, i32* %e + store i32 %f_arg, i32* %f + %tmp = load i32* %a + %tmp1 = mul i32 %tmp, 2 + %tmp2 = load i32* %b + %tmp3 = mul i32 %tmp2, 3 + %tmp4 = add i32 %tmp1, %tmp3 + %tmp5 = load i32* %c + %tmp6 = mul i32 %tmp5, 5 + %tmp7 = add i32 %tmp4, %tmp6 + %tmp8 = load i32* %d + %tmp9 = mul i32 %tmp8, 7 + %tmp10 = add i32 %tmp7, %tmp9 + %tmp11 = load i32* %e + %tmp12 = mul i32 %tmp11, 11 + %tmp13 = add i32 %tmp10, %tmp12 + %tmp14 = load i32* %f + %tmp15 = mul i32 %tmp14, 13 + %tmp16 = add i32 %tmp13, %tmp15 + ret i32 %tmp16 +} +; WIN64: .seh_proc foo3 +; WIN64: pushq %rsi +; WIN64: .seh_pushreg 6 +; WIN64: subq $24, %rsp +; WIN64: .seh_stackalloc 24 +; WIN64: .seh_endprologue +; WIN64: addq $24, %rsp +; WIN64: popq %rsi +; WIN64: ret +; WIN64: .seh_endproc + + +; Check emission of eh handler and handler data +declare i32 @_d_eh_personality(i32, i32, i64, i8*, i8*) +declare void @_d_eh_resume_unwind(i8*) + +declare i32 @bar() + +define i32 @foo4() #0 { +entry: + %step = alloca i32, align 4 + store i32 0, i32* %step + %tmp = load i32* %step + + %tmp1 = invoke i32 @bar() + to label %finally unwind label %landingpad + +finally: + store i32 1, i32* %step + br label %endtryfinally + +landingpad: + %landing_pad = landingpad { i8*, i32 } personality i32 (i32, i32, i64, i8*, i8*)* @_d_eh_personality + cleanup + %tmp3 = extractvalue { i8*, i32 } %landing_pad, 0 + store i32 2, i32* %step + call void @_d_eh_resume_unwind(i8* %tmp3) + unreachable + +endtryfinally: + %tmp10 = load i32* %step + ret i32 %tmp10 +} +; WIN64: .seh_proc foo4 +; WIN64: .seh_handler _d_eh_personality, @unwind, @except +; WIN64: subq $56, %rsp +; WIN64: .seh_stackalloc 56 +; WIN64: .seh_endprologue +; WIN64: addq $56, %rsp +; WIN64: ret +; WIN64: .seh_handlerdata +; WIN64: .seh_endproc