diff --git a/include/llvm/CodeGen/MachineModuleInfo.h b/include/llvm/CodeGen/MachineModuleInfo.h index 584ce65c9d1..f0d0b2dbcdb 100644 --- a/include/llvm/CodeGen/MachineModuleInfo.h +++ b/include/llvm/CodeGen/MachineModuleInfo.h @@ -66,6 +66,7 @@ struct LandingPadInfo { 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) @@ -330,6 +331,11 @@ public: /// 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); diff --git a/lib/CodeGen/AsmPrinter/EHStreamer.cpp b/lib/CodeGen/AsmPrinter/EHStreamer.cpp index f112120c1ab..1bc86f6c222 100644 --- a/lib/CodeGen/AsmPrinter/EHStreamer.cpp +++ b/lib/CodeGen/AsmPrinter/EHStreamer.cpp @@ -121,7 +121,8 @@ computeActionsTable(const SmallVectorImpl &LandingPads, for (unsigned J = NumShared, M = TypeIds.size(); J != M; ++J) { int TypeID = TypeIds[J]; assert(-1 - TypeID < (int)FilterOffsets.size() && "Unknown filter id!"); - int ValueForTypeID = TypeID < 0 ? FilterOffsets[-1 - TypeID] : TypeID; + int ValueForTypeID = + isFilterEHSelector(TypeID) ? FilterOffsets[-1 - TypeID] : TypeID; unsigned SizeTypeID = getSLEB128Size(ValueForTypeID); int NextAction = SizeAction ? -(SizeAction + SizeTypeID) : 0; @@ -269,14 +270,14 @@ computeCallSiteTable(SmallVectorImpl &CallSites, 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 && !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; @@ -576,15 +577,15 @@ void EHStreamer::emitExceptionTable() { // 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 @@ -681,7 +682,7 @@ void EHStreamer::emitTypeInfos(unsigned TTypeEncoding) { unsigned TypeID = *I; if (VerboseAsm) { --Entry; - if (TypeID != 0) + if (isFilterEHSelector(TypeID)) Asm->OutStreamer.AddComment("FilterInfo " + Twine(Entry)); } diff --git a/lib/CodeGen/AsmPrinter/EHStreamer.h b/lib/CodeGen/AsmPrinter/EHStreamer.h index e93055ce865..9b316ff00e9 100644 --- a/lib/CodeGen/AsmPrinter/EHStreamer.h +++ b/lib/CodeGen/AsmPrinter/EHStreamer.h @@ -23,6 +23,8 @@ class MachineModuleInfo; class MachineInstr; class MachineFunction; class AsmPrinter; +class MCSymbol; +class MCSymbolRefExpr; template class SmallVectorImpl; @@ -60,11 +62,11 @@ protected: /// Structure describing an entry in the call-site table. struct CallSiteEntry { // The 'try-range' is BeginLabel .. EndLabel. - MCSymbol *BeginLabel; // zero indicates the start of the function. - MCSymbol *EndLabel; // zero indicates the end of the function. + MCSymbol *BeginLabel; // Null indicates the start of the function. + MCSymbol *EndLabel; // Null 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. + const LandingPadInfo *LPad; // Null indicates that there is no landing pad. unsigned Action; }; @@ -112,6 +114,13 @@ protected: virtual void emitTypeInfos(unsigned TTypeEncoding); + // Helpers for for identifying what kind of clause an EH typeid or selector + // corresponds to. Negative selectors are for filter clauses, the zero + // selector is for cleanups, and positive selectors are for catch clauses. + static bool isFilterEHSelector(int Selector) { return Selector < 0; } + static bool isCleanupEHSelector(int Selector) { return Selector == 0; } + static bool isCatchEHSelector(int Selector) { return Selector > 0; } + public: EHStreamer(AsmPrinter *A); virtual ~EHStreamer(); diff --git a/lib/CodeGen/AsmPrinter/Win64Exception.cpp b/lib/CodeGen/AsmPrinter/Win64Exception.cpp index 0f0ad755835..2138cb9514f 100644 --- a/lib/CodeGen/AsmPrinter/Win64Exception.cpp +++ b/lib/CodeGen/AsmPrinter/Win64Exception.cpp @@ -99,9 +99,156 @@ void Win64Exception::endFunction(const MachineFunction *) { 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(Twine("unexpected personality function: ") + + Per->getName()); + } 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 +/// integer (1, 0, or -1) indicating 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. +/// +/// Possible filter function return values: +/// EXCEPTION_EXECUTE_HANDLER (1): +/// Jump to the landing pad label after cleanups. +/// EXCEPTION_CONTINUE_SEARCH (0): +/// Continue searching this table or continue unwinding. +/// EXCEPTION_CONTINUE_EXECUTION (-1): +/// Resume execution at the trapping PC. +/// +/// Inferred table structure: +/// struct Table { +/// int NumEntries; +/// struct Entry { +/// imagerel32 LabelStart; +/// imagerel32 LabelEnd; +/// imagerel32 FilterOrFinally; // Zero means catch-all. +/// imagerel32 LabelLPad; // Zero means __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 Selector : CSE.LPad->TypeIds) { + // Ignore C++ filter clauses in SEH. + // FIXME: Implement cleanup clauses. + if (isCatchEHSelector(Selector)) + ++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 ordered by selector. The zero selector is used for + // cleanups, so slot zero corresponds to selector 1. + const std::vector &SelectorToFilter = 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 Selector = LPad->TypeIds[E - I - 1]; + MCSymbol *ClauseLabel = LPad->ClauseLabels[I]; + + // Ignore C++ filter clauses in SEH. + // FIXME: Implement cleanup clauses. + if (!isCatchEHSelector(Selector)) + continue; + + Asm->OutStreamer.EmitValue(Begin, 4); + Asm->OutStreamer.EmitValue(End, 4); + if (isCatchEHSelector(Selector)) { + assert(unsigned(Selector - 1) < SelectorToFilter.size()); + const GlobalValue *TI = SelectorToFilter[Selector - 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); + } + } +} diff --git a/lib/CodeGen/AsmPrinter/Win64Exception.h b/lib/CodeGen/AsmPrinter/Win64Exception.h index 538e1328157..b2d5d1bce56 100644 --- a/lib/CodeGen/AsmPrinter/Win64Exception.h +++ b/lib/CodeGen/AsmPrinter/Win64Exception.h @@ -29,6 +29,10 @@ class Win64Exception : public EHStreamer { /// 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. diff --git a/lib/CodeGen/MachineModuleInfo.cpp b/lib/CodeGen/MachineModuleInfo.cpp index baad411e2c5..32d511285eb 100644 --- a/lib/CodeGen/MachineModuleInfo.cpp +++ b/lib/CodeGen/MachineModuleInfo.cpp @@ -452,6 +452,14 @@ void MachineModuleInfo::addCleanup(MachineBasicBlock *LandingPad) { 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) { diff --git a/lib/CodeGen/Passes.cpp b/lib/CodeGen/Passes.cpp index e53e874cb17..28e9f847a9d 100644 --- a/lib/CodeGen/Passes.cpp +++ b/lib/CodeGen/Passes.cpp @@ -449,9 +449,9 @@ void TargetPassConfig::addPassesToHandleExceptions() { case ExceptionHandling::DwarfCFI: case ExceptionHandling::ARM: case ExceptionHandling::ItaniumWinEH: + case ExceptionHandling::MSVC: // FIXME: Needs preparation. addPass(createDwarfEHPass(TM)); break; - case ExceptionHandling::MSVC: // FIXME: Add preparation. case ExceptionHandling::None: addPass(createLowerInvokePass()); diff --git a/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp index 151bc724df6..86a63eea7c2 100644 --- a/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ b/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -2071,10 +2071,14 @@ void SelectionDAGBuilder::visitLandingPad(const LandingPadInst &LP) { // 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()), @@ -2086,6 +2090,27 @@ void SelectionDAGBuilder::visitLandingPad(const LandingPadInst &LP) { 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(TLI.getPointerTy()); + unsigned VReg = FuncInfo.MF->getRegInfo().createVirtualRegister(RC); + unsigned TypeID = DAG.getMachineFunction().getMMI().getTypeIDFor(ClauseGV); + SDValue Sel = DAG.getConstant(TypeID, TLI.getPointerTy()); + 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, diff --git a/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.h b/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.h index 8216a0223cd..c1f483186d9 100644 --- a/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.h +++ b/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.h @@ -713,6 +713,8 @@ public: 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. diff --git a/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp b/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp index 79109b7b8e6..a3f0fa47dd9 100644 --- a/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp +++ b/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" @@ -892,6 +894,8 @@ void SelectionDAGISel::DoInstructionSelection() { void SelectionDAGISel::PrepareEHLandingPad() { MachineBasicBlock *MBB = FuncInfo->MBB; + const TargetRegisterClass *PtrRC = TLI->getRegClassFor(TLI->getPointerTy()); + // Add a label to mark the beginning of the landing pad. Deletion of the // landing pad can thus be detected via the MachineModuleInfo. MCSymbol *Label = MF->getMMI().addLandingPad(MBB); @@ -903,8 +907,66 @@ void SelectionDAGISel::PrepareEHLandingPad() { BuildMI(*MBB, FuncInfo->InsertPt, SDB->getCurDebugLoc(), II) .addSym(Label); + 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(PtrRC); + + // 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; + } + // Mark exception register as live in. - const TargetRegisterClass *PtrRC = TLI->getRegClassFor(TLI->getPointerTy()); if (unsigned Reg = TLI->getExceptionPointerRegister()) FuncInfo->ExceptionPointerVirtReg = MBB->addLiveIn(Reg, PtrRC); diff --git a/test/CodeGen/X86/seh-basic.ll b/test/CodeGen/X86/seh-basic.ll new file mode 100644 index 00000000000..69d70d70948 --- /dev/null +++ b/test/CodeGen/X86/seh-basic.ll @@ -0,0 +1,175 @@ +; 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: .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: .Ltmp11: +; CHECK: callq try_body +; CHECK-NEXT: .Ltmp12: +; CHECK: callq do_nothing_on_unwind +; CHECK: .Ltmp13: +; CHECK: callq try_body +; CHECK-NEXT: .Ltmp14: +; CHECK: retq + +; Landing pad code + +; CHECK: .Ltmp16: +; CHECK: movl $1, %ecx +; CHECK: callq use_selector + +; CHECK: .seh_handlerdata +; CHECK-NEXT: .long 2 +; CHECK-NEXT: .long .Ltmp11@IMGREL +; CHECK-NEXT: .long .Ltmp12@IMGREL+1 +; CHECK-NEXT: .long filt0@IMGREL +; CHECK-NEXT: .long .Ltmp16@IMGREL +; CHECK-NEXT: .long .Ltmp13@IMGREL +; CHECK-NEXT: .long .Ltmp14@IMGREL+1 +; CHECK-NEXT: .long filt0@IMGREL +; CHECK-NEXT: .long .Ltmp16@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: .Ltmp21: +; CHECK: callq try_body +; CHECK-NEXT: .Ltmp22: +; CHECK: callq cannot_unwind +; CHECK: .Ltmp23: +; CHECK: callq try_body +; CHECK-NEXT: .Ltmp24: +; CHECK: retq + +; Landing pad code + +; CHECK: .Ltmp26: +; CHECK: movl $1, %ecx +; CHECK: callq use_selector + +; CHECK: .seh_handlerdata +; CHECK-NEXT: .long 1 +; CHECK-NEXT: .long .Ltmp21@IMGREL +; CHECK-NEXT: .long .Ltmp24@IMGREL+1 +; CHECK-NEXT: .long filt0@IMGREL +; CHECK-NEXT: .long .Ltmp26@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 diff --git a/test/CodeGen/X86/seh-safe-div.ll b/test/CodeGen/X86/seh-safe-div.ll new file mode 100644 index 00000000000..e911df04ded --- /dev/null +++ b/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()