Index: include/llvm/CodeGen/MachineModuleInfo.h =================================================================== --- include/llvm/CodeGen/MachineModuleInfo.h +++ include/llvm/CodeGen/MachineModuleInfo.h @@ -66,6 +66,7 @@ MachineBasicBlock *LandingPadBlock; // Landing pad block. SmallVector BeginLabels; // Labels prior to invoke. SmallVector EndLabels; // Labels after invoke. + SmallVector ClauseLabels; // Labels for each clause. MCSymbol *LandingPadLabel; // Label at beginning of landing pad. const Function *Personality; // Personality function. std::vector TypeIds; // List of type ids (filters negative) @@ -312,6 +313,11 @@ /// void addCleanup(MachineBasicBlock *LandingPad); + /// Add a clause for a landing pad. Returns a new label for the clause. This + /// is used by EH schemes that have more than one landing pad. In this case, + /// each clause gets its own basic block. + MCSymbol *addClauseForLandingPad(MachineBasicBlock *LandingPad); + /// getTypeIDFor - Return the type id for the specified typeinfo. This is /// function wide. unsigned getTypeIDFor(const GlobalValue *TI); Index: include/llvm/MC/MCAsmInfo.h =================================================================== --- include/llvm/MC/MCAsmInfo.h +++ include/llvm/MC/MCAsmInfo.h @@ -47,6 +47,7 @@ SjLj, /// setjmp/longjmp based exceptions ARM, /// ARM EHABI ItaniumWinEH, /// Itanium EH built on Windows unwind info (.pdata and .xdata) + MSVC, /// MSVC compatible exception handling }; namespace LCOMM { Index: lib/CodeGen/AsmPrinter/AsmPrinter.cpp =================================================================== --- lib/CodeGen/AsmPrinter/AsmPrinter.cpp +++ lib/CodeGen/AsmPrinter/AsmPrinter.cpp @@ -242,6 +242,7 @@ ES = new ARMException(this); break; case ExceptionHandling::ItaniumWinEH: + case ExceptionHandling::MSVC: switch (MAI->getWinEHEncodingType()) { default: llvm_unreachable("unsupported unwinding information encoding"); case WinEH::EncodingType::Itanium: Index: lib/CodeGen/AsmPrinter/EHStreamer.h =================================================================== --- lib/CodeGen/AsmPrinter/EHStreamer.h +++ lib/CodeGen/AsmPrinter/EHStreamer.h @@ -23,6 +23,8 @@ class MachineInstr; class MachineFunction; class AsmPrinter; +class MCSymbol; +class MCSymbolRefExpr; template class SmallVectorImpl; @@ -63,8 +65,8 @@ MCSymbol *BeginLabel; // zero indicates the start of the function. MCSymbol *EndLabel; // zero indicates the end of the function. - // The landing pad starts at PadLabel. - MCSymbol *PadLabel; // zero indicates that there is no landing pad. + // LPad contains the landing pad start labels. There may be more than one. + const LandingPadInfo *LPad; // zero indicates that there is no landing pad. unsigned Action; }; @@ -86,7 +88,6 @@ /// form gaps in the table. Entries must be ordered by try-range address. void computeCallSiteTable(SmallVectorImpl &CallSites, - const RangeMapType &PadMap, const SmallVectorImpl &LPs, const SmallVectorImpl &FirstActions); Index: lib/CodeGen/AsmPrinter/EHStreamer.cpp =================================================================== --- lib/CodeGen/AsmPrinter/EHStreamer.cpp +++ lib/CodeGen/AsmPrinter/EHStreamer.cpp @@ -195,9 +195,22 @@ /// table. Entries must be ordered by try-range address. void EHStreamer:: computeCallSiteTable(SmallVectorImpl &CallSites, - const RangeMapType &PadMap, const SmallVectorImpl &LandingPads, const SmallVectorImpl &FirstActions) { + // Invokes and nounwind calls have entries in PadMap (due to being bracketed + // by try-range labels when lowered). Ordinary calls do not, so appropriate + // try-ranges for them need be deduced when using DWARF exception handling. + RangeMapType PadMap; + for (unsigned i = 0, N = LandingPads.size(); i != N; ++i) { + const LandingPadInfo *LandingPad = LandingPads[i]; + for (unsigned j = 0, E = LandingPad->BeginLabels.size(); j != E; ++j) { + MCSymbol *BeginLabel = LandingPad->BeginLabels[j]; + assert(!PadMap.count(BeginLabel) && "Duplicate landing pad labels!"); + PadRange P = { i, j }; + PadMap[BeginLabel] = P; + } + } + // The end label of the previous invoke or nounwind try-range. MCSymbol *LastLabel = nullptr; @@ -208,6 +221,8 @@ // Whether the last CallSite entry was for an invoke. bool PreviousIsInvoke = false; + bool IsSJLJ = Asm->MAI->getExceptionHandlingType() == ExceptionHandling::SjLj; + // Visit all instructions in order of address. for (const auto &MBB : *Asm->MF) { for (const auto &MI : MBB) { @@ -237,7 +252,7 @@ // instruction between the previous try-range and this one may throw, // create a call-site entry with no landing pad for the region between the // try-ranges. - if (SawPotentiallyThrowing && Asm->MAI->usesItaniumLSDAForExceptions()) { + if (SawPotentiallyThrowing && !IsSJLJ) { CallSiteEntry Site = { LastLabel, BeginLabel, nullptr, 0 }; CallSites.push_back(Site); PreviousIsInvoke = false; @@ -254,14 +269,14 @@ CallSiteEntry Site = { BeginLabel, LastLabel, - LandingPad->LandingPadLabel, + LandingPad, FirstActions[P.PadIndex] }; // Try to merge with the previous call-site. SJLJ doesn't do this - if (PreviousIsInvoke && Asm->MAI->usesItaniumLSDAForExceptions()) { + if (PreviousIsInvoke && !IsSJLJ) { CallSiteEntry &Prev = CallSites.back(); - if (Site.PadLabel == Prev.PadLabel && Site.Action == Prev.Action) { + if (Site.LPad == Prev.LPad && Site.Action == Prev.Action) { // Extend the range of the previous entry. Prev.EndLabel = Site.EndLabel; continue; @@ -269,7 +284,7 @@ } // Otherwise, create a new call-site. - if (Asm->MAI->usesItaniumLSDAForExceptions()) + if (!IsSJLJ) CallSites.push_back(Site); else { // SjLj EH must maintain the call sites in the order assigned @@ -287,7 +302,7 @@ // If some instruction between the previous try-range and the end of the // function may throw, create a call-site entry with no landing pad for the // region following the try-range. - if (SawPotentiallyThrowing && Asm->MAI->usesItaniumLSDAForExceptions()) { + if (SawPotentiallyThrowing && IsSJLJ) { CallSiteEntry Site = { LastLabel, nullptr, nullptr, 0 }; CallSites.push_back(Site); } @@ -338,23 +353,9 @@ unsigned SizeActions = computeActionsTable(LandingPads, Actions, FirstActions); - // Invokes and nounwind calls have entries in PadMap (due to being bracketed - // by try-range labels when lowered). Ordinary calls do not, so appropriate - // try-ranges for them need be deduced when using DWARF exception handling. - RangeMapType PadMap; - for (unsigned i = 0, N = LandingPads.size(); i != N; ++i) { - const LandingPadInfo *LandingPad = LandingPads[i]; - for (unsigned j = 0, E = LandingPad->BeginLabels.size(); j != E; ++j) { - MCSymbol *BeginLabel = LandingPad->BeginLabels[j]; - assert(!PadMap.count(BeginLabel) && "Duplicate landing pad labels!"); - PadRange P = { i, j }; - PadMap[BeginLabel] = P; - } - } - // Compute the call-site table. SmallVector CallSites; - computeCallSiteTable(CallSites, PadMap, LandingPads, FirstActions); + computeCallSiteTable(CallSites, LandingPads, FirstActions); // Final tallies. @@ -576,15 +577,15 @@ // Offset of the landing pad, counted in 16-byte bundles relative to the // @LPStart address. - if (!S.PadLabel) { + if (!S.LPad) { if (VerboseAsm) Asm->OutStreamer.AddComment(" has no landing pad"); Asm->OutStreamer.EmitIntValue(0, 4/*size*/); } else { if (VerboseAsm) Asm->OutStreamer.AddComment(Twine(" jumps to ") + - S.PadLabel->getName()); - Asm->EmitLabelDifference(S.PadLabel, EHFuncBeginSym, 4); + S.LPad->LandingPadLabel->getName()); + Asm->EmitLabelDifference(S.LPad->LandingPadLabel, EHFuncBeginSym, 4); } // Offset of the first associated action record, relative to the start of Index: lib/CodeGen/AsmPrinter/Win64Exception.h =================================================================== --- lib/CodeGen/AsmPrinter/Win64Exception.h +++ lib/CodeGen/AsmPrinter/Win64Exception.h @@ -16,6 +16,8 @@ #include "EHStreamer.h" +#include + namespace llvm { class MachineFunction; @@ -29,6 +31,10 @@ /// Per-function flag to indicate if frame moves info should be emitted. bool shouldEmitMoves; + void emitCSpecificHandlerTable(); + + const MCSymbolRefExpr *createImageRel32(const MCSymbol *Value); + public: //===--------------------------------------------------------------------===// // Main entry points. Index: lib/CodeGen/AsmPrinter/Win64Exception.cpp =================================================================== --- lib/CodeGen/AsmPrinter/Win64Exception.cpp +++ lib/CodeGen/AsmPrinter/Win64Exception.cpp @@ -99,9 +99,142 @@ if (shouldEmitPersonality) { Asm->OutStreamer.PushSection(); + + // Emit an UNWIND_INFO struct describing the prologue. Asm->OutStreamer.EmitWinEHHandlerData(); - emitExceptionTable(); + + // Emit either MSVC-compatible tables or the usual Itanium-style LSDA after + // the UNWIND_INFO struct. + if (Asm->MAI->getExceptionHandlingType() == ExceptionHandling::MSVC) { + const Function *Per = MMI->getPersonalities()[MMI->getPersonalityIndex()]; + if (Per->getName() == "__C_specific_handler") + emitCSpecificHandlerTable(); + else + report_fatal_error("unexpected personality function"); + } else { + emitExceptionTable(); + } + Asm->OutStreamer.PopSection(); } Asm->OutStreamer.EmitWinCFIEndProc(); } + +const MCSymbolRefExpr *Win64Exception::createImageRel32(const MCSymbol *Value) { + return MCSymbolRefExpr::Create(Value, MCSymbolRefExpr::VK_COFF_IMGREL32, + Asm->OutContext); +} + +/// 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 +/// are not really RTTI data, but pointers to filter functions that return an +/// EXCEPTION_DISPOSITION (1, 0, or -1) to indicate how to handle the exception. +/// For __finally blocks and other cleanups, the landing pad label is zero, and +/// the filter function is actually a cleanup handler with the same prototype. +/// A catch-all entry is modeled with a null filter function field and a +/// non-zero landing pad label. +/// +/// Table structure: +/// struct Table { +/// int NumEntries; +/// struct Entry { +/// imagerel32 LabelStart; +/// imagerel32 LabelEnd; +/// imagerel32 FilterOrFinally; // Function pointer. +/// imagerel32 LabelLPad; // Zero implies __finally. +/// } Entries[NumEntries]; +/// }; +void Win64Exception::emitCSpecificHandlerTable() { + const std::vector &PadInfos = MMI->getLandingPads(); + + // Simplifying assumptions for first implementation: + // - Cleanups are not implemented. + // - Filters are not implemented. + + // The Itanium LSDA table sorts similar landing pads together to simplify the + // actions table, but we don't need that. + SmallVector LandingPads; + LandingPads.reserve(PadInfos.size()); + for (const auto &LP : PadInfos) + LandingPads.push_back(&LP); + + // Compute label ranges for call sites as we would for the Itanium LSDA, but + // use an all zero action table because we aren't using these actions. + SmallVector FirstActions; + FirstActions.resize(LandingPads.size()); + SmallVector CallSites; + computeCallSiteTable(CallSites, LandingPads, FirstActions); + + MCSymbol *EHFuncBeginSym = + Asm->GetTempSymbol("eh_func_begin", Asm->getFunctionNumber()); + MCSymbol *EHFuncEndSym = + Asm->GetTempSymbol("eh_func_end", Asm->getFunctionNumber()); + + // Emit the number of table entries. + unsigned NumEntries = 0; + for (const CallSiteEntry &CSE : CallSites) { + if (!CSE.LPad) + continue; // Ignore gaps. + for (int TypeID : CSE.LPad->TypeIds) + if (TypeID >= 0) + ++NumEntries; + } + Asm->OutStreamer.EmitIntValue(NumEntries, 4); + + // Emit the four-label records for each call site entry. The table has to be + // sorted in layout order, and the call sites should already be sorted. + for (const CallSiteEntry &CSE : CallSites) { + // Ignore gaps. Unlike the Itanium model, unwinding through a frame without + // an EH table entry will propagate the exception rather than terminating + // the program. + if (!CSE.LPad) + continue; + const LandingPadInfo *LPad = CSE.LPad; + + // Compute the label range. We may reuse the function begin and end labels + // rather than forming new ones. + const MCExpr *Begin = + createImageRel32(CSE.BeginLabel ? CSE.BeginLabel : EHFuncBeginSym); + const MCExpr *End; + if (CSE.EndLabel) { + // The interval is half-open, so we have to add one to include the return + // address of the last invoke in the range. + End = MCBinaryExpr::CreateAdd(createImageRel32(CSE.EndLabel), + MCConstantExpr::Create(1, Asm->OutContext), + Asm->OutContext); + } else { + End = createImageRel32(EHFuncEndSym); + } + + // These aren't really type info globals, they are actually pointers to + // filter functions. + const std::vector &TypeInfos = MMI->getTypeInfos(); + + // Do a parallel iteration across typeids and clause labels, skipping filter + // clauses. + assert(LPad->TypeIds.size() == LPad->ClauseLabels.size()); + for (size_t I = 0, E = LPad->TypeIds.size(); I < E; ++I) { + // AddLandingPadInfo stores the clauses in reverse, but there is a FIXME + // to change that. + int TypeID = LPad->TypeIds[E - I - 1]; + MCSymbol *ClauseLabel = LPad->ClauseLabels[I]; + assert(TypeID >= 0 && "SEH personality functions don't support filter " + "clauses in landing pads"); + + Asm->OutStreamer.EmitValue(Begin, 4); + Asm->OutStreamer.EmitValue(End, 4); + if (TypeID == 0) + Asm->OutStreamer.EmitIntValue(0, 4); + else { + assert(unsigned(TypeID - 1) < TypeInfos.size()); + const GlobalValue *TI = TypeInfos[TypeID - 1]; + if (TI) // Emit the filter function pointer. + Asm->OutStreamer.EmitValue(createImageRel32(Asm->getSymbol(TI)), 4); + else // Otherwise, this is a "catch i8* null", or catch all. + Asm->OutStreamer.EmitIntValue(0, 4); + } + Asm->OutStreamer.EmitValue(createImageRel32(ClauseLabel), 4); + } + } +} Index: lib/CodeGen/MachineModuleInfo.cpp =================================================================== --- lib/CodeGen/MachineModuleInfo.cpp +++ lib/CodeGen/MachineModuleInfo.cpp @@ -452,6 +452,14 @@ LP.TypeIds.push_back(0); } +MCSymbol * +MachineModuleInfo::addClauseForLandingPad(MachineBasicBlock *LandingPad) { + MCSymbol *ClauseLabel = Context.CreateTempSymbol(); + LandingPadInfo &LP = getOrCreateLandingPadInfo(LandingPad); + LP.ClauseLabels.push_back(ClauseLabel); + return ClauseLabel; +} + /// TidyLandingPads - Remap landing pad labels and remove any deleted landing /// pads. void MachineModuleInfo::TidyLandingPads(DenseMap *LPMap) { Index: lib/CodeGen/Passes.cpp =================================================================== --- lib/CodeGen/Passes.cpp +++ lib/CodeGen/Passes.cpp @@ -430,6 +430,7 @@ case ExceptionHandling::DwarfCFI: case ExceptionHandling::ARM: case ExceptionHandling::ItaniumWinEH: + case ExceptionHandling::MSVC: // FIXME: Needs preparation. addPass(createDwarfEHPass(TM)); break; case ExceptionHandling::None: Index: lib/CodeGen/SelectionDAG/SelectionDAGBuilder.h =================================================================== --- lib/CodeGen/SelectionDAG/SelectionDAGBuilder.h +++ lib/CodeGen/SelectionDAG/SelectionDAGBuilder.h @@ -699,6 +699,8 @@ void visitJumpTable(JumpTable &JT); void visitJumpTableHeader(JumpTable &JT, JumpTableHeader &JTH, MachineBasicBlock *SwitchBB); + unsigned visitLandingPadClauseBB(GlobalValue *ClauseGV, + MachineBasicBlock *LPadMBB); private: // These all get lowered before this pass. Index: lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp =================================================================== --- lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -2062,13 +2062,17 @@ // Get the two live-in registers as SDValues. The physregs have already been // copied into virtual registers. SDValue Ops[2]; - Ops[0] = DAG.getZExtOrTrunc( - DAG.getCopyFromReg(DAG.getEntryNode(), getCurSDLoc(), - FuncInfo.ExceptionPointerVirtReg, TLI.getPointerTy()), - getCurSDLoc(), ValueVTs[0]); + if (FuncInfo.ExceptionPointerVirtReg) { + Ops[0] = DAG.getZExtOrTrunc( + DAG.getCopyFromReg(DAG.getEntryNode(), getCurSDLoc(), + FuncInfo.ExceptionPointerVirtReg, TLI.getPointerTy()), + getCurSDLoc(), ValueVTs[0]); + } else { + Ops[0] = DAG.getConstant(0, TLI.getPointerTy()); + } Ops[1] = DAG.getZExtOrTrunc( DAG.getCopyFromReg(DAG.getEntryNode(), getCurSDLoc(), - FuncInfo.ExceptionSelectorVirtReg, TLI.getPointerTy()), + FuncInfo.ExceptionSelectorVirtReg, MVT::i32), getCurSDLoc(), ValueVTs[1]); // Merge into one. @@ -2077,6 +2081,27 @@ setValue(&LP, Res); } +unsigned +SelectionDAGBuilder::visitLandingPadClauseBB(GlobalValue *ClauseGV, + MachineBasicBlock *LPadBB) { + SDValue Chain = getControlRoot(); + + // Get the typeid that we will dispatch on later. + const TargetLowering &TLI = DAG.getTargetLoweringInfo(); + const TargetRegisterClass *RC = TLI.getRegClassFor(MVT::i32); + unsigned VReg = FuncInfo.MF->getRegInfo().createVirtualRegister(RC); + unsigned TypeID = DAG.getMachineFunction().getMMI().getTypeIDFor(ClauseGV); + SDValue Sel = DAG.getConstant(TypeID, MVT::i32); + Chain = DAG.getCopyToReg(Chain, getCurSDLoc(), VReg, Sel); + + // Branch to the main landing pad block. + MachineBasicBlock *ClauseMBB = FuncInfo.MBB; + ClauseMBB->addSuccessor(LPadBB); + DAG.setRoot(DAG.getNode(ISD::BR, getCurSDLoc(), MVT::Other, Chain, + DAG.getBasicBlock(LPadBB))); + return VReg; +} + /// handleSmallSwitchCaseRange - Emit a series of specific tests (suitable for /// small case ranges). bool SelectionDAGBuilder::handleSmallSwitchRange(CaseRec& CR, Index: lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp =================================================================== --- lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp +++ lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp @@ -19,6 +19,7 @@ #include "llvm/Analysis/AliasAnalysis.h" #include "llvm/Analysis/BranchProbabilityInfo.h" #include "llvm/Analysis/CFG.h" +#include "llvm/CodeGen/Analysis.h" #include "llvm/CodeGen/FastISel.h" #include "llvm/CodeGen/FunctionLoweringInfo.h" #include "llvm/CodeGen/GCMetadata.h" @@ -40,6 +41,7 @@ #include "llvm/IR/Intrinsics.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Module.h" +#include "llvm/MC/MCAsmInfo.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/Debug.h" #include "llvm/Support/ErrorHandling.h" @@ -891,6 +893,7 @@ /// do other setup for EH landing-pad blocks. void SelectionDAGISel::PrepareEHLandingPad() { MachineBasicBlock *MBB = FuncInfo->MBB; + const TargetRegisterClass *I32RC = TLI->getRegClassFor(MVT::i32); // Add a label to mark the beginning of the landing pad. Deletion of the // landing pad can thus be detected via the MachineModuleInfo. @@ -903,14 +906,74 @@ BuildMI(*MBB, FuncInfo->InsertPt, SDB->getCurDebugLoc(), II) .addSym(Label); - // Mark exception register as live in. + if (TM.getMCAsmInfo()->getExceptionHandlingType() == + ExceptionHandling::MSVC) { + // Make virtual registers and a series of labels that fill in values for the + // clauses. + auto &RI = MF->getRegInfo(); + FuncInfo->ExceptionSelectorVirtReg = RI.createVirtualRegister(I32RC); + + // Get all invoke BBs that will unwind into the clause BBs. + SmallVector InvokeBBs(MBB->pred_begin(), + MBB->pred_end()); + + // Emit separate machine basic blocks with separate labels for each clause + // before the main landing pad block. + const BasicBlock *LLVMBB = MBB->getBasicBlock(); + const LandingPadInst *LPadInst = LLVMBB->getLandingPadInst(); + MachineInstrBuilder SelectorPHI = BuildMI( + *MBB, MBB->begin(), SDB->getCurDebugLoc(), TII->get(TargetOpcode::PHI), + FuncInfo->ExceptionSelectorVirtReg); + for (unsigned I = 0, E = LPadInst->getNumClauses(); I != E; ++I) { + MachineBasicBlock *ClauseBB = MF->CreateMachineBasicBlock(LLVMBB); + MF->insert(MBB, ClauseBB); + + // Add the edge from the invoke to the clause. + for (MachineBasicBlock *InvokeBB : InvokeBBs) + InvokeBB->addSuccessor(ClauseBB); + + // Mark the clause as a landing pad or MI passes will delete it. + ClauseBB->setIsLandingPad(); + + GlobalValue *ClauseGV = ExtractTypeInfo(LPadInst->getClause(I)); + + // Start the BB with a label. + MCSymbol *ClauseLabel = MF->getMMI().addClauseForLandingPad(MBB); + BuildMI(*ClauseBB, ClauseBB->begin(), SDB->getCurDebugLoc(), II) + .addSym(ClauseLabel); + + // Construct a simple BB that defines a register with the typeid constant. + FuncInfo->MBB = ClauseBB; + FuncInfo->InsertPt = ClauseBB->end(); + unsigned VReg = SDB->visitLandingPadClauseBB(ClauseGV, MBB); + CurDAG->setRoot(SDB->getRoot()); + SDB->clear(); + CodeGenAndEmitDAG(); + + // Add the typeid virtual register to the phi in the main landing pad. + SelectorPHI.addReg(VReg).addMBB(ClauseBB); + } + + // Remove the edge from the invoke to the lpad. + for (MachineBasicBlock *InvokeBB : InvokeBBs) + InvokeBB->removeSuccessor(MBB); + + // Restore FuncInfo back to its previous state and select the main landing + // pad block. + FuncInfo->MBB = MBB; + FuncInfo->InsertPt = MBB->end(); + return; + } + const TargetRegisterClass *PtrRC = TLI->getRegClassFor(TLI->getPointerTy()); + + // Mark exception register as live in. if (unsigned Reg = TLI->getExceptionPointerRegister()) FuncInfo->ExceptionPointerVirtReg = MBB->addLiveIn(Reg, PtrRC); // Mark exception selector register as live in. if (unsigned Reg = TLI->getExceptionSelectorRegister()) - FuncInfo->ExceptionSelectorVirtReg = MBB->addLiveIn(Reg, PtrRC); + FuncInfo->ExceptionSelectorVirtReg = MBB->addLiveIn(Reg, I32RC); } /// isFoldedOrDeadInstruction - Return true if the specified instruction is Index: lib/Target/X86/MCTargetDesc/X86MCAsmInfo.cpp =================================================================== --- lib/Target/X86/MCTargetDesc/X86MCAsmInfo.cpp +++ lib/Target/X86/MCTargetDesc/X86MCAsmInfo.cpp @@ -137,9 +137,11 @@ PrivateGlobalPrefix = ".L"; PointerSize = 8; WinEHEncodingType = WinEH::EncodingType::Itanium; - ExceptionsType = ExceptionHandling::ItaniumWinEH; } + // Use MSVC-compatible EH data. + ExceptionsType = ExceptionHandling::MSVC; + AssemblerDialect = AsmWriterFlavor; TextAlignFillValue = 0x90; Index: test/CodeGen/X86/seh-basic.ll =================================================================== --- /dev/null +++ test/CodeGen/X86/seh-basic.ll @@ -0,0 +1,178 @@ +; RUN: llc -mtriple x86_64-pc-windows-msvc < %s | FileCheck %s + +define void @two_invoke_merged() { +entry: + invoke void @try_body() + to label %again unwind label %lpad + +again: + invoke void @try_body() + to label %done unwind label %lpad + +done: + ret void + +lpad: + %vals = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__C_specific_handler to i8*) + catch i8* bitcast (i32 (i8*, i8*)* @filt0 to i8*) + catch i8* bitcast (i32 (i8*, i8*)* @filt1 to i8*) + %sel = extractvalue { i8*, i32 } %vals, 1 + call void @use_selector(i32 %sel) + ret void +} + +; Normal path code + +; CHECK-LABEL: {{^}}two_invoke_merged: +; CHECK: .seh_proc two_invoke_merged +; CHECK: .seh_handler __C_specific_handler, @unwind, @except +; CHECK: .Ltmp0: +; CHECK: callq try_body +; CHECK-NEXT: .Ltmp1: +; CHECK: .Ltmp2: +; CHECK: callq try_body +; CHECK-NEXT: .Ltmp3: +; CHECK: retq + +; Landing pad code + +; CHECK: .Ltmp5: +; CHECK: movl $1, %ecx +; CHECK: jmp +; CHECK: .Ltmp6: +; CHECK: movl $2, %ecx +; CHECK: callq use_selector +; CHECK: retq + +; CHECK: .seh_handlerdata +; CHECK-NEXT: .long 2 +; CHECK-NEXT: .long .Ltmp0@IMGREL +; CHECK-NEXT: .long .Ltmp3@IMGREL+1 +; CHECK-NEXT: .long filt0@IMGREL +; CHECK-NEXT: .long .Ltmp5@IMGREL +; CHECK-NEXT: .long .Ltmp0@IMGREL +; CHECK-NEXT: .long .Ltmp3@IMGREL+1 +; CHECK-NEXT: .long filt1@IMGREL +; CHECK-NEXT: .long .Ltmp6@IMGREL +; CHECK: .text +; CHECK: .seh_endproc + +define void @two_invoke_gap() { +entry: + invoke void @try_body() + to label %again unwind label %lpad + +again: + call void @do_nothing_on_unwind() + invoke void @try_body() + to label %done unwind label %lpad + +done: + ret void + +lpad: + %vals = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__C_specific_handler to i8*) + catch i8* bitcast (i32 (i8*, i8*)* @filt0 to i8*) + %sel = extractvalue { i8*, i32 } %vals, 1 + call void @use_selector(i32 %sel) + ret void +} + +; Normal path code + +; CHECK-LABEL: {{^}}two_invoke_gap: +; CHECK: .seh_proc two_invoke_gap +; CHECK: .seh_handler __C_specific_handler, @unwind, @except +; CHECK: .Ltmp9: +; CHECK: callq try_body +; CHECK-NEXT: .Ltmp10: +; CHECK: callq do_nothing_on_unwind +; CHECK: .Ltmp11: +; CHECK: callq try_body +; CHECK-NEXT: .Ltmp12: +; CHECK: retq + +; Landing pad code + +; CHECK: .Ltmp14: +; CHECK: movl $1, %ecx +; CHECK: callq use_selector +; CHECK: retq + +; CHECK: .seh_handlerdata +; CHECK-NEXT: .long 2 +; CHECK-NEXT: .long .Ltmp9@IMGREL +; CHECK-NEXT: .long .Ltmp10@IMGREL+1 +; CHECK-NEXT: .long filt0@IMGREL +; CHECK-NEXT: .long .Ltmp14@IMGREL +; CHECK-NEXT: .long .Ltmp11@IMGREL +; CHECK-NEXT: .long .Ltmp12@IMGREL+1 +; CHECK-NEXT: .long filt0@IMGREL +; CHECK-NEXT: .long .Ltmp14@IMGREL +; CHECK: .text +; CHECK: .seh_endproc + +define void @two_invoke_nounwind_gap() { +entry: + invoke void @try_body() + to label %again unwind label %lpad + +again: + call void @cannot_unwind() + invoke void @try_body() + to label %done unwind label %lpad + +done: + ret void + +lpad: + %vals = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__C_specific_handler to i8*) + catch i8* bitcast (i32 (i8*, i8*)* @filt0 to i8*) + %sel = extractvalue { i8*, i32 } %vals, 1 + call void @use_selector(i32 %sel) + ret void +} + +; Normal path code + +; CHECK-LABEL: {{^}}two_invoke_nounwind_gap: +; CHECK: .seh_proc two_invoke_nounwind_gap +; CHECK: .seh_handler __C_specific_handler, @unwind, @except +; CHECK: .Ltmp17: +; CHECK: callq try_body +; CHECK-NEXT: .Ltmp18: +; CHECK: callq cannot_unwind +; CHECK: .Ltmp19: +; CHECK: callq try_body +; CHECK-NEXT: .Ltmp20: +; CHECK: retq + +; Landing pad code + +; CHECK: .Ltmp22: +; CHECK: movl $1, %ecx +; CHECK: callq use_selector +; CHECK: retq + +; CHECK: .seh_handlerdata +; CHECK-NEXT: .long 1 +; CHECK-NEXT: .long .Ltmp17@IMGREL +; CHECK-NEXT: .long .Ltmp20@IMGREL+1 +; CHECK-NEXT: .long filt0@IMGREL +; CHECK-NEXT: .long .Ltmp22@IMGREL +; CHECK: .text +; CHECK: .seh_endproc + +declare void @try_body() +declare void @do_nothing_on_unwind() +declare void @cannot_unwind() nounwind +declare void @use_selector(i32) + +declare i32 @filt0(i8* %eh_info, i8* %rsp) +declare i32 @filt1(i8* %eh_info, i8* %rsp) + +declare void @handler0() +declare void @handler1() + +declare i32 @__C_specific_handler(...) +declare i32 @llvm.eh.typeid.for(i8*) readnone nounwind Index: test/CodeGen/X86/seh-safe-div.ll =================================================================== --- /dev/null +++ test/CodeGen/X86/seh-safe-div.ll @@ -0,0 +1,196 @@ +; RUN: llc -mtriple x86_64-pc-windows-msvc < %s | FileCheck %s + +; This test case is also intended to be run manually as a complete functional +; test. It should link, print something, and exit zero rather than crashing. +; It is the hypothetical lowering of a C source program that looks like: +; +; int safe_div(int *n, int *d) { +; int r; +; __try { +; __try { +; r = *n / *d; +; } __except(GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION) { +; puts("EXCEPTION_ACCESS_VIOLATION"); +; r = -1; +; } +; } __except(GetExceptionCode() == EXCEPTION_INT_DIVIDE_BY_ZERO) { +; puts("EXCEPTION_INT_DIVIDE_BY_ZERO"); +; r = -2; +; } +; return r; +; } + +@str1 = internal constant [27 x i8] c"EXCEPTION_ACCESS_VIOLATION\00" +@str2 = internal constant [29 x i8] c"EXCEPTION_INT_DIVIDE_BY_ZERO\00" + +define i32 @safe_div(i32* %n, i32* %d) { +entry: + %r = alloca i32, align 4 + invoke void @try_body(i32* %r, i32* %n, i32* %d) + to label %__try.cont unwind label %lpad + +lpad: + %vals = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__C_specific_handler to i8*) + catch i8* bitcast (i32 (i8*, i8*)* @safe_div_filt0 to i8*) + catch i8* bitcast (i32 (i8*, i8*)* @safe_div_filt1 to i8*) + %ehptr = extractvalue { i8*, i32 } %vals, 0 + %sel = extractvalue { i8*, i32 } %vals, 1 + %filt0_val = call i32 @llvm.eh.typeid.for(i8* bitcast (i32 (i8*, i8*)* @safe_div_filt0 to i8*)) + %is_filt0 = icmp eq i32 %sel, %filt0_val + br i1 %is_filt0, label %handler0, label %eh.dispatch1 + +eh.dispatch1: + %filt1_val = call i32 @llvm.eh.typeid.for(i8* bitcast (i32 (i8*, i8*)* @safe_div_filt1 to i8*)) + %is_filt1 = icmp eq i32 %sel, %filt1_val + br i1 %is_filt1, label %handler1, label %eh.resume + +handler0: + call void @puts(i8* getelementptr ([27 x i8]* @str1, i32 0, i32 0)) + store i32 -1, i32* %r, align 4 + br label %__try.cont + +handler1: + call void @puts(i8* getelementptr ([29 x i8]* @str2, i32 0, i32 0)) + store i32 -2, i32* %r, align 4 + br label %__try.cont + +eh.resume: + resume { i8*, i32 } %vals + +__try.cont: + %safe_ret = load i32* %r, align 4 + ret i32 %safe_ret +} + +; Normal path code + +; CHECK: {{^}}safe_div: +; CHECK: .seh_proc safe_div +; CHECK: .seh_handler __C_specific_handler, @unwind, @except +; CHECK: .Ltmp0: +; CHECK: leaq [[rloc:.*\(%rsp\)]], %rcx +; CHECK: callq try_body +; CHECK-NEXT: .Ltmp1 +; CHECK: .LBB0_7: +; CHECK: movl [[rloc]], %eax +; CHECK: retq + +; Landing pad code + +; CHECK: .Ltmp3: +; CHECK: movl $1, %[[sel:[a-z]+]] +; CHECK: .Ltmp4 +; CHECK: movl $2, %[[sel]] +; CHECK: .L{{.*}}: +; CHECK: cmpl $1, %[[sel]] + +; CHECK: # %handler0 +; CHECK: callq puts +; CHECK: movl $-1, [[rloc]] +; CHECK: jmp .LBB0_7 + +; CHECK: cmpl $2, %[[sel]] + +; CHECK: # %handler1 +; CHECK: callq puts +; CHECK: movl $-2, [[rloc]] +; CHECK: jmp .LBB0_7 + +; FIXME: EH preparation should not call _Unwind_Resume. +; CHECK: callq _Unwind_Resume +; CHECK: ud2 + +; CHECK: .seh_handlerdata +; CHECK: .long 2 +; CHECK: .long .Ltmp0@IMGREL +; CHECK: .long .Ltmp1@IMGREL+1 +; CHECK: .long safe_div_filt0@IMGREL +; CHECK: .long .Ltmp3@IMGREL +; CHECK: .long .Ltmp0@IMGREL +; CHECK: .long .Ltmp1@IMGREL+1 +; CHECK: .long safe_div_filt1@IMGREL +; CHECK: .long .Ltmp4@IMGREL +; CHECK: .text +; CHECK: .seh_endproc + + +define void @try_body(i32* %r, i32* %n, i32* %d) { +entry: + %0 = load i32* %n, align 4 + %1 = load i32* %d, align 4 + %div = sdiv i32 %0, %1 + store i32 %div, i32* %r, align 4 + ret void +} + +; The prototype of these filter functions is: +; int filter(EXCEPTION_POINTERS *eh_ptrs, void *rbp); + +; The definition of EXCEPTION_POINTERS is: +; typedef struct _EXCEPTION_POINTERS { +; EXCEPTION_RECORD *ExceptionRecord; +; CONTEXT *ContextRecord; +; } EXCEPTION_POINTERS; + +; The definition of EXCEPTION_RECORD is: +; typedef struct _EXCEPTION_RECORD { +; DWORD ExceptionCode; +; ... +; } EXCEPTION_RECORD; + +; The exception code can be retreived with two loads, one for the record +; pointer and one for the code. The values of local variables can be +; accessed via rbp, but that would require additional not yet implemented LLVM +; support. + +define i32 @safe_div_filt0(i8* %eh_ptrs, i8* %rbp) { + %eh_ptrs_c = bitcast i8* %eh_ptrs to i32** + %eh_rec = load i32** %eh_ptrs_c + %eh_code = load i32* %eh_rec + ; EXCEPTION_ACCESS_VIOLATION = 0xC0000005 + %cmp = icmp eq i32 %eh_code, 3221225477 + %filt.res = zext i1 %cmp to i32 + ret i32 %filt.res +} + +define i32 @safe_div_filt1(i8* %eh_ptrs, i8* %rbp) { + %eh_ptrs_c = bitcast i8* %eh_ptrs to i32** + %eh_rec = load i32** %eh_ptrs_c + %eh_code = load i32* %eh_rec + ; EXCEPTION_INT_DIVIDE_BY_ZERO = 0xC0000094 + %cmp = icmp eq i32 %eh_code, 3221225620 + %filt.res = zext i1 %cmp to i32 + ret i32 %filt.res +} + +@str_result = internal constant [21 x i8] c"safe_div result: %d\0A\00" + +define i32 @main() { + %d.addr = alloca i32, align 4 + %n.addr = alloca i32, align 4 + + store i32 10, i32* %n.addr, align 4 + store i32 2, i32* %d.addr, align 4 + %r1 = call i32 @safe_div(i32* %n.addr, i32* %d.addr) + call void (i8*, ...)* @printf(i8* getelementptr ([21 x i8]* @str_result, i32 0, i32 0), i32 %r1) + + store i32 10, i32* %n.addr, align 4 + store i32 0, i32* %d.addr, align 4 + %r2 = call i32 @safe_div(i32* %n.addr, i32* %d.addr) + call void (i8*, ...)* @printf(i8* getelementptr ([21 x i8]* @str_result, i32 0, i32 0), i32 %r2) + + %r3 = call i32 @safe_div(i32* %n.addr, i32* null) + call void (i8*, ...)* @printf(i8* getelementptr ([21 x i8]* @str_result, i32 0, i32 0), i32 %r3) + ret i32 0 +} + +define void @_Unwind_Resume() { + call void @abort() + unreachable +} + +declare i32 @__C_specific_handler(...) +declare i32 @llvm.eh.typeid.for(i8*) readnone nounwind +declare void @puts(i8*) +declare void @printf(i8*, ...) +declare void @abort()