diff --git a/include/llvm/CodeGen/WinEHFuncInfo.h b/include/llvm/CodeGen/WinEHFuncInfo.h index a9071acb94c..369faddd647 100644 --- a/include/llvm/CodeGen/WinEHFuncInfo.h +++ b/include/llvm/CodeGen/WinEHFuncInfo.h @@ -14,9 +14,10 @@ #ifndef LLVM_CODEGEN_WINEHFUNCINFO_H #define LLVM_CODEGEN_WINEHFUNCINFO_H +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/PointerUnion.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/TinyPtrVector.h" -#include "llvm/ADT/DenseMap.h" namespace llvm { class AllocaInst; @@ -120,17 +121,32 @@ struct WinEHUnwindMapEntry { const Value *Cleanup; }; +typedef PointerUnion MBBOrBasicBlock; +typedef PointerUnion ValueOrMBB; + +/// Similar to WinEHUnwindMapEntry, but supports SEH filters. +struct SEHUnwindMapEntry { + /// If unwinding continues through this handler, transition to the handler at + /// this state. This indexes into SEHUnwindMap. + int ToState = -1; + + /// Holds the filter expression function. + const Function *Filter = nullptr; + + /// Holds the __except or __finally basic block. + MBBOrBasicBlock Handler; +}; + struct WinEHHandlerType { int Adjectives; GlobalVariable *TypeDescriptor; int CatchObjRecoverIdx; - const Value *Handler; - MachineBasicBlock *HandlerMBB; + ValueOrMBB Handler; }; struct WinEHTryBlockMapEntry { - int TryLow; - int TryHigh; + int TryLow = -1; + int TryHigh = -1; int CatchHigh = -1; SmallVector HandlerArray; }; @@ -147,6 +163,7 @@ struct WinEHFuncInfo { DenseMap HandlerBaseState; SmallVector UnwindMap; SmallVector TryBlockMap; + SmallVector SEHUnwindMap; SmallVector, 4> IPToStateList; int UnwindHelpFrameIdx = INT_MAX; int UnwindHelpFrameOffset = -1; @@ -169,5 +186,7 @@ struct WinEHFuncInfo { void calculateWinCXXEHStateNumbers(const Function *ParentFn, WinEHFuncInfo &FuncInfo); +void calculateSEHStateNumbers(const Function *ParentFn, + WinEHFuncInfo &FuncInfo); } #endif // LLVM_CODEGEN_WINEHFUNCINFO_H diff --git a/lib/CodeGen/AsmPrinter/WinException.cpp b/lib/CodeGen/AsmPrinter/WinException.cpp index aedac5467c6..e706d73dee2 100644 --- a/lib/CodeGen/AsmPrinter/WinException.cpp +++ b/lib/CodeGen/AsmPrinter/WinException.cpp @@ -161,7 +161,7 @@ void WinException::endFunction(const MachineFunction *MF) { // Emit the tables appropriate to the personality function in use. If we // don't recognize the personality, assume it uses an Itanium-style LSDA. if (Per == EHPersonality::MSVC_Win64SEH) - emitCSpecificHandlerTable(); + emitCSpecificHandlerTable(MF); else if (Per == EHPersonality::MSVC_X86SEH) emitExceptHandlerTable(MF); else if (Per == EHPersonality::MSVC_CXX) @@ -222,9 +222,13 @@ const MCExpr *WinException::create32bitRef(const Value *V) { /// imagerel32 LabelLPad; // Zero means __finally. /// } Entries[NumEntries]; /// }; -void WinException::emitCSpecificHandlerTable() { +void WinException::emitCSpecificHandlerTable(const MachineFunction *MF) { const std::vector &PadInfos = MMI->getLandingPads(); + WinEHFuncInfo &FuncInfo = MMI->getWinEHFuncInfo(MF->getFunction()); + if (!FuncInfo.SEHUnwindMap.empty()) + report_fatal_error("x64 SEH tables not yet implemented"); + // Simplifying assumptions for first implementation: // - Cleanups are not implemented. // - Filters are not implemented. @@ -309,6 +313,15 @@ void WinException::emitCSpecificHandlerTable() { } } +/// 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.is()) + return Handler.get()->getSymbol(); + else + return Asm->getSymbol(cast(Handler.get())); +} + void WinException::emitCXXFrameHandler3Table(const MachineFunction *MF) { const Function *F = MF->getFunction(); const Function *ParentF = MMI->getWinEHParent(F); @@ -422,9 +435,9 @@ void WinException::emitCXXFrameHandler3Table(const MachineFunction *MF) { int CatchHigh = TBME.CatchHigh; if (CatchHigh == -1) { for (WinEHHandlerType &HT : TBME.HandlerArray) - CatchHigh = std::max( - CatchHigh, - FuncInfo.CatchHandlerMaxState[cast(HT.Handler)]); + CatchHigh = + std::max(CatchHigh, FuncInfo.CatchHandlerMaxState[cast( + HT.Handler.get())]); } assert(TBME.TryLow <= TBME.TryHigh); @@ -464,13 +477,12 @@ void WinException::emitCXXFrameHandler3Table(const MachineFunction *MF) { FrameAllocOffsetRef = MCConstantExpr::create(0, Asm->OutContext); } - OS.EmitIntValue(HT.Adjectives, 4); // Adjectives - OS.EmitValue(create32bitRef(HT.TypeDescriptor), 4); // Type - OS.EmitValue(FrameAllocOffsetRef, 4); // CatchObjOffset - if (HT.HandlerMBB) // Handler - OS.EmitValue(create32bitRef(HT.HandlerMBB->getSymbol()), 4); - else - OS.EmitValue(create32bitRef(HT.Handler), 4); + MCSymbol *HandlerSym = getMCSymbolForMBBOrGV(Asm, HT.Handler); + + OS.EmitIntValue(HT.Adjectives, 4); // Adjectives + OS.EmitValue(create32bitRef(HT.TypeDescriptor), 4); // Type + OS.EmitValue(FrameAllocOffsetRef, 4); // CatchObjOffset + OS.EmitValue(create32bitRef(HandlerSym), 4); // Handler if (shouldEmitPersonality) { if (FuncInfo.CatchHandlerParentFrameObjOffset.empty()) { @@ -482,7 +494,8 @@ void WinException::emitCXXFrameHandler3Table(const MachineFunction *MF) { } else { MCSymbol *ParentFrameOffset = Asm->OutContext.getOrCreateParentFrameOffsetSymbol( - GlobalValue::getRealLinkageName(HT.Handler->getName())); + GlobalValue::getRealLinkageName( + HT.Handler.get()->getName())); const MCSymbolRefExpr *ParentFrameOffsetRef = MCSymbolRefExpr::create(ParentFrameOffset, Asm->OutContext); OS.EmitValue(ParentFrameOffsetRef, 4); // ParentFrameOffset @@ -642,6 +655,19 @@ void WinException::emitExceptHandlerTable(const MachineFunction *MF) { BaseState = -2; } + if (!FuncInfo.SEHUnwindMap.empty()) { + for (SEHUnwindMapEntry &UME : FuncInfo.SEHUnwindMap) { + MCSymbol *ExceptOrFinally = + UME.Handler.get()->getSymbol(); + OS.EmitIntValue(UME.ToState, 4); // ToState + OS.EmitValue(create32bitRef(UME.Filter), 4); // Filter + OS.EmitValue(create32bitRef(ExceptOrFinally), 4); // Except/Finally + } + return; + } + // FIXME: The following code is for the old landingpad-based SEH + // implementation. Remove it when possible. + // Build a list of pointers to LandingPadInfos and then sort by WinEHState. const std::vector &PadInfos = MMI->getLandingPads(); SmallVector LPads; diff --git a/lib/CodeGen/AsmPrinter/WinException.h b/lib/CodeGen/AsmPrinter/WinException.h index 7a80984c32e..aa159f5cf6e 100644 --- a/lib/CodeGen/AsmPrinter/WinException.h +++ b/lib/CodeGen/AsmPrinter/WinException.h @@ -36,7 +36,7 @@ class LLVM_LIBRARY_VISIBILITY WinException : public EHStreamer { /// True if this is a 64-bit target and we should use image relative offsets. bool useImageRel32 = false; - void emitCSpecificHandlerTable(); + void emitCSpecificHandlerTable(const MachineFunction *MF); /// Emit the EH table data for 32-bit and 64-bit functions using /// the __CxxFrameHandler3 personality. diff --git a/lib/CodeGen/SelectionDAG/FunctionLoweringInfo.cpp b/lib/CodeGen/SelectionDAG/FunctionLoweringInfo.cpp index 7bbb194ac19..15793f666ea 100644 --- a/lib/CodeGen/SelectionDAG/FunctionLoweringInfo.cpp +++ b/lib/CodeGen/SelectionDAG/FunctionLoweringInfo.cpp @@ -287,21 +287,31 @@ void FunctionLoweringInfo::set(const Function &fn, MachineFunction &mf, addSEHHandlersForLPads(LPads); } + // Calculate state numbers if we haven't already. WinEHFuncInfo &EHInfo = MMI.getWinEHFuncInfo(&fn); if (Personality == EHPersonality::MSVC_CXX) { - // Calculate state numbers and then map from funclet BBs to MBBs. const Function *WinEHParentFn = MMI.getWinEHParent(&fn); calculateWinCXXEHStateNumbers(WinEHParentFn, EHInfo); + } else { + const Function *WinEHParentFn = MMI.getWinEHParent(&fn); + calculateSEHStateNumbers(WinEHParentFn, EHInfo); + } + + // Map all BB references in the EH data to MBBs. for (WinEHTryBlockMapEntry &TBME : EHInfo.TryBlockMap) for (WinEHHandlerType &H : TBME.HandlerArray) - if (const auto *BB = dyn_cast(H.Handler)) - H.HandlerMBB = MBBMap[BB]; - // If there's an explicit EH registration node on the stack, record its - // frame index. - if (EHInfo.EHRegNode && EHInfo.EHRegNode->getParent()->getParent() == Fn) { - assert(StaticAllocaMap.count(EHInfo.EHRegNode)); - EHInfo.EHRegNodeFrameIndex = StaticAllocaMap[EHInfo.EHRegNode]; + if (const auto *BB = + dyn_cast(H.Handler.get())) + H.Handler = MBBMap[BB]; + for (SEHUnwindMapEntry &UME : EHInfo.SEHUnwindMap) { + const BasicBlock *BB = UME.Handler.get(); + UME.Handler = MBBMap[BB]; } + // If there's an explicit EH registration node on the stack, record its + // frame index. + if (EHInfo.EHRegNode && EHInfo.EHRegNode->getParent()->getParent() == Fn) { + assert(StaticAllocaMap.count(EHInfo.EHRegNode)); + EHInfo.EHRegNodeFrameIndex = StaticAllocaMap[EHInfo.EHRegNode]; } // Copy the state numbers to LandingPadInfo for the current function, which diff --git a/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp index 1df4245bf0b..6fba18d60b2 100644 --- a/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ b/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -1989,7 +1989,6 @@ void SelectionDAGBuilder::visitInvoke(const InvokeInst &I) { // and catchendpads for successors. MachineBasicBlock *Return = FuncInfo.MBBMap[I.getSuccessor(0)]; const BasicBlock *EHPadBB = I.getSuccessor(1); - bool IsLandingPad = EHPadBB->isLandingPad(); const Value *Callee(I.getCalledValue()); const Function *Fn = dyn_cast(Callee); @@ -2025,16 +2024,26 @@ void SelectionDAGBuilder::visitInvoke(const InvokeInst &I) { // Catchpads are conditional branches that add real MBB destinations and // continue the loop. EH "end" pads are not real BBs and simply continue. SmallVector UnwindDests; + bool IsMSVCCXX = classifyEHPersonality(FuncInfo.Fn->getPersonalityFn()) == + EHPersonality::MSVC_CXX; while (EHPadBB) { const Instruction *Pad = EHPadBB->getFirstNonPHI(); - if (isa(Pad) || isa(Pad)) { - assert(FuncInfo.MBBMap[EHPadBB]); - // Stop on cleanup pads and landingpads. + if (isa(Pad)) { + // Stop on landingpads. They are not funclets. UnwindDests.push_back(FuncInfo.MBBMap[EHPadBB]); break; + } else if (isa(Pad) || isa(Pad)) { + // Stop on cleanup pads. Cleanups are always funclet entries for all known + // personalities. + UnwindDests.push_back(FuncInfo.MBBMap[EHPadBB]); + UnwindDests.back()->setIsEHFuncletEntry(); + break; } else if (const auto *CPI = dyn_cast(Pad)) { // Add the catchpad handler to the possible destinations. UnwindDests.push_back(FuncInfo.MBBMap[CPI->getNormalDest()]); + // In MSVC C++, catchblocks are funclets and need prologues. + if (IsMSVCCXX) + UnwindDests.back()->setIsEHFuncletEntry(); EHPadBB = CPI->getUnwindDest(); } else if (const auto *CEPI = dyn_cast(Pad)) { EHPadBB = CEPI->getUnwindDest(); @@ -2043,13 +2052,11 @@ void SelectionDAGBuilder::visitInvoke(const InvokeInst &I) { } } - // Update successor info + // Update successor info. // FIXME: The weights for catchpads will be wrong. addSuccessorWithWeight(InvokeMBB, Return); - for (auto *UnwindDest : UnwindDests) { + for (MachineBasicBlock *UnwindDest : UnwindDests) { UnwindDest->setIsEHPad(); - if (!IsLandingPad) - UnwindDest->setIsEHFuncletEntry(); addSuccessorWithWeight(InvokeMBB, UnwindDest); } diff --git a/lib/CodeGen/WinEHPrepare.cpp b/lib/CodeGen/WinEHPrepare.cpp index c333c074900..ed3cd5d400b 100644 --- a/lib/CodeGen/WinEHPrepare.cpp +++ b/lib/CodeGen/WinEHPrepare.cpp @@ -2616,7 +2616,6 @@ static void addTryBlockMapEntry(WinEHFuncInfo &FuncInfo, int TryLow, cast(CS->getAggregateElement(1)->stripPointerCasts()); } HT.Handler = CPI->getNormalDest(); - HT.HandlerMBB = nullptr; // FIXME: Pass CPI->getArgOperand(1). HT.CatchObjRecoverIdx = -1; TBME.HandlerArray.push_back(HT); @@ -2645,7 +2644,8 @@ void WinEHNumbering::createTryBlockMapEntry(int TryLow, int TryHigh, continue; int N; for (N = 0; N < NumHandlers; ++N) { - if (Entry.HandlerArray[N].Handler != Handlers[N]->getHandlerBlockOrFunc()) + if (Entry.HandlerArray[N].Handler.get() != + Handlers[N]->getHandlerBlockOrFunc()) break; // breaks out of inner loop } // If all the handlers match, this is what we were looking for. @@ -2692,7 +2692,6 @@ void WinEHNumbering::createTryBlockMapEntry(int TryLow, int TryHigh, cast(CS->getAggregateElement(1)->stripPointerCasts()); } HT.Handler = cast(CH->getHandlerBlockOrFunc()); - HT.HandlerMBB = nullptr; HT.CatchObjRecoverIdx = CH->getExceptionVarIndex(); TBME.HandlerArray.push_back(HT); } @@ -2949,13 +2948,45 @@ void WinEHNumbering::findActionRootLPads(const Function &F) { } } -static const BasicBlock *getSingleCatchPadPredecessor(const BasicBlock &BB) { - for (const BasicBlock *PredBlock : predecessors(&BB)) - if (isa(PredBlock->getFirstNonPHI())) - return PredBlock; +static const CatchPadInst *getSingleCatchPadPredecessor(const BasicBlock *BB) { + for (const BasicBlock *PredBlock : predecessors(BB)) + if (auto *CPI = dyn_cast(PredBlock->getFirstNonPHI())) + return CPI; return nullptr; } +/// Find all the catchpads that feed directly into the catchendpad. Frontends +/// using this personality should ensure that each catchendpad and catchpad has +/// one or zero catchpad predecessors. +/// +/// The following C++ generates the IR after it: +/// try { +/// } catch (A) { +/// } catch (B) { +/// } +/// +/// IR: +/// %catchpad.A +/// catchpad [i8* A typeinfo] +/// to label %catch.A unwind label %catchpad.B +/// %catchpad.B +/// catchpad [i8* B typeinfo] +/// to label %catch.B unwind label %endcatches +/// %endcatches +/// catchendblock unwind to caller +void findCatchPadsForCatchEndPad( + const BasicBlock *CatchEndBB, + SmallVectorImpl &Handlers) { + const CatchPadInst *CPI = getSingleCatchPadPredecessor(CatchEndBB); + while (CPI) { + Handlers.push_back(CPI); + CPI = getSingleCatchPadPredecessor(CPI->getParent()); + } + // We've pushed these back into reverse source order. Reverse them to get + // the list back into source order. + std::reverse(Handlers.begin(), Handlers.end()); +} + // Given BB which ends in an unwind edge, return the EHPad that this BB belongs // to. If the unwind edge came from an invoke, return null. static const BasicBlock *getEHPadFromPredecessor(const BasicBlock *BB) { @@ -2970,9 +3001,9 @@ static const BasicBlock *getEHPadFromPredecessor(const BasicBlock *BB) { return cast(TI)->getCleanupPad()->getParent(); } -static void calculateExplicitStateNumbers(WinEHFuncInfo &FuncInfo, - const BasicBlock &BB, - int ParentState) { +static void calculateExplicitCXXStateNumbers(WinEHFuncInfo &FuncInfo, + const BasicBlock &BB, + int ParentState) { assert(BB.isEHPad()); const Instruction *FirstNonPHI = BB.getFirstNonPHI(); // All catchpad instructions will be handled when we process their @@ -2981,36 +3012,30 @@ static void calculateExplicitStateNumbers(WinEHFuncInfo &FuncInfo, return; if (isa(FirstNonPHI)) { - const BasicBlock *TryPad = &BB; - const BasicBlock *LastTryPad = nullptr; SmallVector Handlers; - do { - LastTryPad = TryPad; - TryPad = getSingleCatchPadPredecessor(*TryPad); - if (TryPad) - Handlers.push_back(cast(TryPad->getFirstNonPHI())); - } while (TryPad); - // We've pushed these back into reverse source order. Reverse them to get - // the list back into source order. - std::reverse(Handlers.begin(), Handlers.end()); + findCatchPadsForCatchEndPad(&BB, Handlers); + const BasicBlock *FirstTryPad = Handlers.front()->getParent(); int TryLow = addUnwindMapEntry(FuncInfo, ParentState, nullptr); FuncInfo.EHPadStateMap[Handlers.front()] = TryLow; - for (const BasicBlock *PredBlock : predecessors(LastTryPad)) + for (const BasicBlock *PredBlock : predecessors(FirstTryPad)) if ((PredBlock = getEHPadFromPredecessor(PredBlock))) - calculateExplicitStateNumbers(FuncInfo, *PredBlock, TryLow); + calculateExplicitCXXStateNumbers(FuncInfo, *PredBlock, TryLow); int CatchLow = addUnwindMapEntry(FuncInfo, ParentState, nullptr); + + // catchpads are separate funclets in C++ EH due to the way rethrow works. + // In SEH, they aren't, so no invokes will unwind to the catchendpad. FuncInfo.EHPadStateMap[FirstNonPHI] = CatchLow; int TryHigh = CatchLow - 1; for (const BasicBlock *PredBlock : predecessors(&BB)) if ((PredBlock = getEHPadFromPredecessor(PredBlock))) - calculateExplicitStateNumbers(FuncInfo, *PredBlock, CatchLow); + calculateExplicitCXXStateNumbers(FuncInfo, *PredBlock, CatchLow); int CatchHigh = FuncInfo.getLastStateNumber(); addTryBlockMapEntry(FuncInfo, TryLow, TryHigh, CatchHigh, Handlers); - DEBUG(dbgs() << "TryLow[" << LastTryPad->getName() << "]: " << TryLow + DEBUG(dbgs() << "TryLow[" << FirstTryPad->getName() << "]: " << TryLow << '\n'); - DEBUG(dbgs() << "TryHigh[" << LastTryPad->getName() << "]: " << TryHigh + DEBUG(dbgs() << "TryHigh[" << FirstTryPad->getName() << "]: " << TryHigh << '\n'); - DEBUG(dbgs() << "CatchHigh[" << LastTryPad->getName() << "]: " << CatchHigh + DEBUG(dbgs() << "CatchHigh[" << FirstTryPad->getName() << "]: " << CatchHigh << '\n'); } else if (isa(FirstNonPHI)) { int CleanupState = addUnwindMapEntry(FuncInfo, ParentState, &BB); @@ -3019,7 +3044,7 @@ static void calculateExplicitStateNumbers(WinEHFuncInfo &FuncInfo, << BB.getName() << '\n'); for (const BasicBlock *PredBlock : predecessors(&BB)) if ((PredBlock = getEHPadFromPredecessor(PredBlock))) - calculateExplicitStateNumbers(FuncInfo, *PredBlock, CleanupState); + calculateExplicitCXXStateNumbers(FuncInfo, *PredBlock, CleanupState); } else if (isa(FirstNonPHI)) { report_fatal_error("Not yet implemented!"); } else { @@ -3027,6 +3052,103 @@ static void calculateExplicitStateNumbers(WinEHFuncInfo &FuncInfo, } } +static int addSEHHandler(WinEHFuncInfo &FuncInfo, int ParentState, + const Function *Filter, const BasicBlock *Handler) { + SEHUnwindMapEntry Entry; + Entry.ToState = ParentState; + Entry.Filter = Filter; + Entry.Handler = Handler; + FuncInfo.SEHUnwindMap.push_back(Entry); + return FuncInfo.SEHUnwindMap.size() - 1; +} + +static void calculateExplicitSEHStateNumbers(WinEHFuncInfo &FuncInfo, + const BasicBlock &BB, + int ParentState) { + assert(BB.isEHPad()); + const Instruction *FirstNonPHI = BB.getFirstNonPHI(); + // All catchpad instructions will be handled when we process their + // respective catchendpad instruction. + if (isa(FirstNonPHI)) + return; + + if (isa(FirstNonPHI)) { + // Extract the filter function and the __except basic block and create a + // state for them. + SmallVector Handlers; + findCatchPadsForCatchEndPad(&BB, Handlers); + assert(Handlers.size() == 1 && + "SEH doesn't have multiple handlers per __try"); + const CatchPadInst *CPI = Handlers.front(); + const BasicBlock *CatchPadBB = CPI->getParent(); + const Function *Filter = + cast(CPI->getArgOperand(0)->stripPointerCasts()); + int TryState = + addSEHHandler(FuncInfo, ParentState, Filter, CPI->getNormalDest()); + + // Everything in the __try block uses TryState as its parent state. + FuncInfo.EHPadStateMap[CPI] = TryState; + DEBUG(dbgs() << "Assigning state #" << TryState << " to BB " + << CatchPadBB->getName() << '\n'); + for (const BasicBlock *PredBlock : predecessors(CatchPadBB)) + if ((PredBlock = getEHPadFromPredecessor(PredBlock))) + calculateExplicitSEHStateNumbers(FuncInfo, *PredBlock, TryState); + + // Everything in the __except block unwinds to ParentState, just like code + // outside the __try. + FuncInfo.EHPadStateMap[FirstNonPHI] = ParentState; + DEBUG(dbgs() << "Assigning state #" << ParentState << " to BB " + << BB.getName() << '\n'); + for (const BasicBlock *PredBlock : predecessors(&BB)) + if ((PredBlock = getEHPadFromPredecessor(PredBlock))) + calculateExplicitSEHStateNumbers(FuncInfo, *PredBlock, ParentState); + } else if (isa(FirstNonPHI)) { + int CleanupState = + addSEHHandler(FuncInfo, ParentState, /*Filter=*/nullptr, &BB); + FuncInfo.EHPadStateMap[FirstNonPHI] = CleanupState; + DEBUG(dbgs() << "Assigning state #" << CleanupState << " to BB " + << BB.getName() << '\n'); + for (const BasicBlock *PredBlock : predecessors(&BB)) + if ((PredBlock = getEHPadFromPredecessor(PredBlock))) + calculateExplicitSEHStateNumbers(FuncInfo, *PredBlock, CleanupState); + } else if (isa(FirstNonPHI)) { + report_fatal_error("Not yet implemented!"); + } else { + llvm_unreachable("unexpected EH Pad!"); + } +} + +/// Check if the EH Pad unwinds to caller. Cleanups are a little bit of a +/// special case because we have to look at the cleanupret instruction that uses +/// the cleanuppad. +static bool doesEHPadUnwindToCaller(const Instruction *EHPad) { + auto *CPI = dyn_cast(EHPad); + if (!CPI) + return EHPad->mayThrow(); + + // This cleanup does not return or unwind, so we say it unwinds to caller. + if (CPI->use_empty()) + return true; + + const Instruction *User = CPI->user_back(); + if (auto *CRI = dyn_cast(User)) + return CRI->unwindsToCaller(); + return cast(User)->unwindsToCaller(); +} + +void llvm::calculateSEHStateNumbers(const Function *ParentFn, + WinEHFuncInfo &FuncInfo) { + // Don't compute state numbers twice. + if (!FuncInfo.SEHUnwindMap.empty()) + return; + + for (const BasicBlock &BB : *ParentFn) { + if (!BB.isEHPad() || !doesEHPadUnwindToCaller(BB.getFirstNonPHI())) + continue; + calculateExplicitSEHStateNumbers(FuncInfo, BB, -1); + } +} + void llvm::calculateWinCXXEHStateNumbers(const Function *ParentFn, WinEHFuncInfo &FuncInfo) { // Return if it's already been done. @@ -3041,28 +3163,9 @@ void llvm::calculateWinCXXEHStateNumbers(const Function *ParentFn, // Skip cleanupendpads; they are exits, not entries. if (isa(FirstNonPHI)) continue; - // Check if the EH Pad has no exceptional successors (i.e. it unwinds to - // caller). Cleanups are a little bit of a special case because their - // control flow cannot be determined by looking at the pad but instead by - // the pad's users. - bool HasNoSuccessors = false; - if (FirstNonPHI->mayThrow()) { - HasNoSuccessors = true; - } else if (auto *CPI = dyn_cast(FirstNonPHI)) { - if (CPI->use_empty()) { - HasNoSuccessors = true; - } else { - const Instruction *User = CPI->user_back(); - if (auto *CRI = dyn_cast(User)) - HasNoSuccessors = CRI->unwindsToCaller(); - else - HasNoSuccessors = cast(User)->unwindsToCaller(); - } - } - - if (!HasNoSuccessors) + if (!doesEHPadUnwindToCaller(FirstNonPHI)) continue; - calculateExplicitStateNumbers(FuncInfo, BB, -1); + calculateExplicitCXXStateNumbers(FuncInfo, BB, -1); IsExplicit = true; } diff --git a/lib/Target/X86/X86ISelLowering.cpp b/lib/Target/X86/X86ISelLowering.cpp index b793213d955..0225b081edf 100644 --- a/lib/Target/X86/X86ISelLowering.cpp +++ b/lib/Target/X86/X86ISelLowering.cpp @@ -16880,6 +16880,16 @@ SDValue X86TargetLowering::LowerCATCHRET(SDValue Op, SelectionDAG &DAG) const { SDLoc DL(Op); MVT PtrVT = getPointerTy(DAG.getDataLayout()); + + MachineFunction &MF = DAG.getMachineFunction(); + if (isAsynchronousEHPersonality( + classifyEHPersonality(MF.getFunction()->getPersonalityFn()))) { + // For SEH, codegen catchret as a branch for now. + // FIXME: Insert something to restore the frame. + return DAG.getNode(ISD::BR, DL, MVT::Other, Chain, Dest); + } + + unsigned ReturnReg = (PtrVT == MVT::i64 ? X86::RAX : X86::EAX); // Load the address of the destination block. diff --git a/lib/Target/X86/X86WinEHState.cpp b/lib/Target/X86/X86WinEHState.cpp index ccec7743b3b..38dc5db3430 100644 --- a/lib/Target/X86/X86WinEHState.cpp +++ b/lib/Target/X86/X86WinEHState.cpp @@ -68,9 +68,10 @@ private: void unlinkExceptionRegistration(IRBuilder<> &Builder); void addCXXStateStores(Function &F, WinEHFuncInfo &FuncInfo); void addSEHStateStores(Function &F, WinEHFuncInfo &FuncInfo); - void addCXXStateStoresToFunclet(Value *ParentRegNode, WinEHFuncInfo &FuncInfo, - Function &F, int BaseState); + void addStateStoresToFunclet(Value *ParentRegNode, WinEHFuncInfo &FuncInfo, + Function &F, int BaseState); void insertStateNumberStore(Value *ParentRegNode, Instruction *IP, int State); + void insertRestoreFrame(BasicBlock *BB); Value *emitEHLSDA(IRBuilder<> &Builder, Function *F); @@ -91,6 +92,7 @@ private: Function *FrameRecover = nullptr; Function *FrameAddress = nullptr; Function *FrameEscape = nullptr; + Function *RestoreFrame = nullptr; // Per-function state EHPersonality Personality = EHPersonality::Unknown; @@ -123,6 +125,8 @@ bool WinEHStatePass::doInitialization(Module &M) { FrameEscape = Intrinsic::getDeclaration(TheModule, Intrinsic::localescape); FrameRecover = Intrinsic::getDeclaration(TheModule, Intrinsic::localrecover); FrameAddress = Intrinsic::getDeclaration(TheModule, Intrinsic::frameaddress); + RestoreFrame = + Intrinsic::getDeclaration(TheModule, Intrinsic::x86_seh_restoreframe); return false; } @@ -414,7 +418,7 @@ void WinEHStatePass::addCXXStateStores(Function &F, WinEHFuncInfo &FuncInfo) { calculateWinCXXEHStateNumbers(&F, FuncInfo); // The base state for the parent is -1. - addCXXStateStoresToFunclet(RegNode, FuncInfo, F, -1); + addStateStoresToFunclet(RegNode, FuncInfo, F, -1); // Set up RegNodeEscapeIndex int RegNodeEscapeIndex = escapeRegNode(F); @@ -434,7 +438,7 @@ void WinEHStatePass::addCXXStateStores(Function &F, WinEHFuncInfo &FuncInfo) { FrameRecover, {FI8, ParentFP, Builder.getInt32(RegNodeEscapeIndex)}); RecoveredRegNode = Builder.CreateBitCast(RecoveredRegNode, RegNodeTy->getPointerTo(0)); - addCXXStateStoresToFunclet(RecoveredRegNode, FuncInfo, *Handler, BaseState); + addStateStoresToFunclet(RecoveredRegNode, FuncInfo, *Handler, BaseState); } } @@ -470,11 +474,17 @@ int WinEHStatePass::escapeRegNode(Function &F) { return Args.size() - 1; } -void WinEHStatePass::addCXXStateStoresToFunclet(Value *ParentRegNode, - WinEHFuncInfo &FuncInfo, - Function &F, int BaseState) { - Function *RestoreFrame = - Intrinsic::getDeclaration(TheModule, Intrinsic::x86_seh_restoreframe); +void WinEHStatePass::insertRestoreFrame(BasicBlock *BB) { + Instruction *Start = BB->getFirstInsertionPt(); + if (match(Start, m_Intrinsic())) + return; + IRBuilder<> Builder(Start); + Builder.CreateCall(RestoreFrame, {}); +} + +void WinEHStatePass::addStateStoresToFunclet(Value *ParentRegNode, + WinEHFuncInfo &FuncInfo, + Function &F, int BaseState) { // Iterate all the instructions and emit state number stores. for (BasicBlock &BB : F) { for (Instruction &I : BB) { @@ -494,16 +504,14 @@ void WinEHStatePass::addCXXStateStoresToFunclet(Value *ParentRegNode, } } - // Insert calls to llvm.x86.seh.restoreframe at catchret destinations. - if (auto *CR = dyn_cast(BB.getTerminator())) { - Instruction *Start = CR->getSuccessor()->begin(); - assert(!isa(Start) && - "winehprepare failed to demote phi after catchret"); - if (match(Start, m_Intrinsic())) - continue; - IRBuilder<> Builder(Start); - Builder.CreateCall(RestoreFrame, {}); - } + // Insert calls to llvm.x86.seh.restoreframe at catchret destinations. In + // SEH, insert them before the catchret. + // FIXME: We should probably do this as part of catchret lowering in the + // DAG. + if (auto *CR = dyn_cast(BB.getTerminator())) + insertRestoreFrame(Personality == EHPersonality::MSVC_X86SEH + ? CR->getParent() + : CR->getSuccessor()); } } @@ -519,6 +527,23 @@ void WinEHStatePass::addSEHStateStores(Function &F, WinEHFuncInfo &FuncInfo) { int RegNodeEscapeIndex = escapeRegNode(F); FuncInfo.EHRegNodeEscapeIndex = RegNodeEscapeIndex; + // If this funciton uses the new EH IR, use the explicit state numbering + // algorithm and return early. + bool UsesLPads = false; + for (BasicBlock &BB : F) { + if (BB.isLandingPad()) { + UsesLPads = true; + break; + } + } + if (!UsesLPads) { + calculateSEHStateNumbers(&F, FuncInfo); + addStateStoresToFunclet(RegNode, FuncInfo, F, -1); + return; + } + // FIXME: Delete the rest of this code and clean things up when new EH is + // done. + // Iterate all the instructions and emit state number stores. int CurState = 0; SmallPtrSet ExceptBlocks; diff --git a/test/CodeGen/X86/win32-seh-catchpad.ll b/test/CodeGen/X86/win32-seh-catchpad.ll new file mode 100644 index 00000000000..1d635dc5711 --- /dev/null +++ b/test/CodeGen/X86/win32-seh-catchpad.ll @@ -0,0 +1,219 @@ +; RUN: llc < %s | FileCheck %s + +target datalayout = "e-m:x-p:32:32-i64:64-f80:32-n8:16:32-a:0:32-S32" +target triple = "i686-pc-windows-msvc" + +define void @try_except() #0 personality i8* bitcast (i32 (...)* @_except_handler3 to i8*) { +entry: + %__exception_code = alloca i32, align 4 + call void (...) @llvm.localescape(i32* %__exception_code) + invoke void @f(i32 1) #3 + to label %invoke.cont unwind label %catch.dispatch + +catch.dispatch: ; preds = %entry + %0 = catchpad [i8* bitcast (i32 ()* @try_except_filter_catchall to i8*)] to label %__except.ret unwind label %catchendblock + +__except.ret: ; preds = %catch.dispatch + catchret %0 to label %__except + +__except: ; preds = %__except.ret + call void @f(i32 2) + br label %__try.cont + +__try.cont: ; preds = %__except, %invoke.cont + call void @f(i32 3) + ret void + +catchendblock: ; preds = %catch.dispatch + catchendpad unwind to caller + +invoke.cont: ; preds = %entry + br label %__try.cont +} + +; CHECK-LABEL: _try_except: +; Store state #0 +; CHECK: movl $0, -[[state:[0-9]+]](%ebp) +; CHECK: movl $1, (%esp) +; CHECK: calll _f +; CHECK: movl $-1, -[[state]](%ebp) +; CHECK: movl $3, (%esp) +; CHECK: calll _f +; CHECK: retl + +; __except +; CHECK: movl $-1, -[[state]](%ebp) +; CHECK: movl $2, (%esp) +; CHECK: calll _f + +; CHECK: .section .xdata,"dr" +; CHECK: L__ehtable$try_except: +; CHECK: .long -1 +; CHECK: .long _try_except_filter_catchall +; CHECK: .long LBB0_1 + +define internal i32 @try_except_filter_catchall() #0 { +entry: + %0 = call i8* @llvm.frameaddress(i32 1) + %1 = call i8* @llvm.x86.seh.recoverfp(i8* bitcast (void ()* @try_except to i8*), i8* %0) + %2 = call i8* @llvm.localrecover(i8* bitcast (void ()* @try_except to i8*), i8* %1, i32 0) + %__exception_code = bitcast i8* %2 to i32* + %3 = getelementptr inbounds i8, i8* %0, i32 -20 + %4 = bitcast i8* %3 to i8** + %5 = load i8*, i8** %4, align 4 + %6 = bitcast i8* %5 to { i32*, i8* }* + %7 = getelementptr inbounds { i32*, i8* }, { i32*, i8* }* %6, i32 0, i32 0 + %8 = load i32*, i32** %7, align 4 + %9 = load i32, i32* %8, align 4 + store i32 %9, i32* %__exception_code, align 4 + ret i32 1 +} + +define void @nested_exceptions() #0 personality i8* bitcast (i32 (...)* @_except_handler3 to i8*) { +entry: + %__exception_code = alloca i32, align 4 + call void (...) @llvm.localescape(i32* %__exception_code) + invoke void @crash() #3 + to label %__try.cont unwind label %catch.dispatch + +catch.dispatch: ; preds = %entry + %0 = catchpad [i8* bitcast (i32 ()* @nested_exceptions_filter_catchall to i8*)] to label %__except.ret unwind label %catchendblock + +__except.ret: ; preds = %catch.dispatch + catchret %0 to label %__try.cont + +__try.cont: ; preds = %entry, %__except.ret + invoke void @crash() #3 + to label %__try.cont.9 unwind label %catch.dispatch.5 + +catch.dispatch.5: ; preds = %__try.cont + %1 = catchpad [i8* bitcast (i32 ()* @nested_exceptions_filter_catchall to i8*)] to label %__except.ret.7 unwind label %catchendblock.6 + +__except.ret.7: ; preds = %catch.dispatch.5 + catchret %1 to label %__try.cont.9 + +__try.cont.9: ; preds = %__try.cont, %__except.ret.7 + invoke void @crash() #3 + to label %__try.cont.15 unwind label %catch.dispatch.11 + +catch.dispatch.11: ; preds = %catchendblock, %catchendblock.6, %__try.cont.9 + %2 = catchpad [i8* bitcast (i32 ()* @nested_exceptions_filter_catchall to i8*)] to label %__except.ret.13 unwind label %catchendblock.12 + +__except.ret.13: ; preds = %catch.dispatch.11 + catchret %2 to label %__try.cont.15 + +__try.cont.15: ; preds = %__try.cont.9, %__except.ret.13 + invoke void @crash() #3 + to label %__try.cont.35 unwind label %catch.dispatch.17 + +catch.dispatch.17: ; preds = %catchendblock.12, %__try.cont.15 + %3 = catchpad [i8* bitcast (i32 ()* @nested_exceptions_filter_catchall to i8*)] to label %__except.ret.19 unwind label %catchendblock.18 + +__except.ret.19: ; preds = %catch.dispatch.17 + catchret %3 to label %__except.20 + +__except.20: ; preds = %__except.ret.19 + invoke void @crash() #3 + to label %__try.cont.27 unwind label %catch.dispatch.23 + +catch.dispatch.23: ; preds = %__except.20 + %4 = catchpad [i8* bitcast (i32 ()* @nested_exceptions_filter_catchall to i8*)] to label %__except.ret.25 unwind label %catchendblock.24 + +__except.ret.25: ; preds = %catch.dispatch.23 + catchret %4 to label %__try.cont.27 + +__try.cont.27: ; preds = %__except.20, %__except.ret.25 + invoke void @crash() #3 + to label %__try.cont.35 unwind label %catch.dispatch.30 + +catch.dispatch.30: ; preds = %__try.cont.27 + %5 = catchpad [i8* bitcast (i32 ()* @nested_exceptions_filter_catchall to i8*)] to label %__except.ret.32 unwind label %catchendblock.31 + +__except.ret.32: ; preds = %catch.dispatch.30 + catchret %5 to label %__try.cont.35 + +__try.cont.35: ; preds = %__try.cont.15, %__try.cont.27, %__except.ret.32 + ret void + +catchendblock.31: ; preds = %catch.dispatch.30 + catchendpad unwind to caller + +catchendblock.24: ; preds = %catch.dispatch.23 + catchendpad unwind to caller + +catchendblock.18: ; preds = %catch.dispatch.17 + catchendpad unwind to caller + +catchendblock.12: ; preds = %catch.dispatch.11 + catchendpad unwind label %catch.dispatch.17 + +catchendblock.6: ; preds = %catch.dispatch.5 + catchendpad unwind label %catch.dispatch.11 + +catchendblock: ; preds = %catch.dispatch + catchendpad unwind label %catch.dispatch.11 +} + +; This table is equivalent to the one produced by MSVC, even if it isn't in +; quite the same order. + +; CHECK-LABEL: _nested_exceptions: +; CHECK: L__ehtable$nested_exceptions: +; CHECK: .long -1 +; CHECK: .long _nested_exceptions_filter_catchall +; CHECK: .long LBB +; CHECK: .long -1 +; CHECK: .long _nested_exceptions_filter_catchall +; CHECK: .long LBB +; CHECK: .long -1 +; CHECK: .long _nested_exceptions_filter_catchall +; CHECK: .long LBB +; CHECK: .long 2 +; CHECK: .long _nested_exceptions_filter_catchall +; CHECK: .long LBB +; CHECK: .long 3 +; CHECK: .long _nested_exceptions_filter_catchall +; CHECK: .long LBB +; CHECK: .long 3 +; CHECK: .long _nested_exceptions_filter_catchall +; CHECK: .long LBB + +declare void @crash() #0 + +define internal i32 @nested_exceptions_filter_catchall() #0 { +entry: + %0 = call i8* @llvm.frameaddress(i32 1) + %1 = call i8* @llvm.x86.seh.recoverfp(i8* bitcast (void ()* @nested_exceptions to i8*), i8* %0) + %2 = call i8* @llvm.localrecover(i8* bitcast (void ()* @nested_exceptions to i8*), i8* %1, i32 0) + %__exception_code3 = bitcast i8* %2 to i32* + %3 = getelementptr inbounds i8, i8* %0, i32 -20 + %4 = bitcast i8* %3 to i8** + %5 = load i8*, i8** %4, align 4 + %6 = bitcast i8* %5 to { i32*, i8* }* + %7 = getelementptr inbounds { i32*, i8* }, { i32*, i8* }* %6, i32 0, i32 0 + %8 = load i32*, i32** %7, align 4 + %9 = load i32, i32* %8, align 4 + store i32 %9, i32* %__exception_code3, align 4 + ret i32 1 +} + +; Function Attrs: nounwind readnone +declare i8* @llvm.frameaddress(i32) #1 + +; Function Attrs: nounwind readnone +declare i8* @llvm.x86.seh.recoverfp(i8*, i8*) #1 + +; Function Attrs: nounwind readnone +declare i8* @llvm.localrecover(i8*, i8*, i32) #1 + +declare void @f(i32) #0 + +declare i32 @_except_handler3(...) + +; Function Attrs: nounwind +declare void @llvm.localescape(...) #2 + +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" } +attributes #1 = { nounwind readnone } +attributes #2 = { nounwind } +attributes #3 = { noinline }