From 64386621ec978386e0238fc16990861c5d4d98c1 Mon Sep 17 00:00:00 2001 From: David Majnemer Date: Tue, 31 Mar 2015 22:35:44 +0000 Subject: [PATCH] [WinEH] Generate .xdata for catch handlers This lets us catch exceptions in simple cases. N.B. Things that do not work include (but are not limited to): - Throwing from within a catch handler. - Catching an object with a named catch parameter. - 'CatchHigh' is fictitious, we aren't sure of its purpose. - We aren't entirely efficient with regards to the number of EH states that we generate. - IP-to-State tables are sensitive to the order of emission. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@233767 91177308-0d34-0410-b5e6-96231b3b80d8 --- docs/ExceptionHandling.rst | 11 - include/llvm/CodeGen/WinEHFuncInfo.h | 6 +- include/llvm/IR/Intrinsics.td | 4 - include/llvm/MC/MCContext.h | 1 + lib/CodeGen/AsmPrinter/Win64Exception.cpp | 34 ++- .../SelectionDAG/FunctionLoweringInfo.cpp | 135 +++++++++--- .../SelectionDAG/SelectionDAGBuilder.cpp | 12 - lib/CodeGen/WinEHPrepare.cpp | 10 - lib/IR/Verifier.cpp | 7 - lib/MC/MCContext.cpp | 5 + lib/Target/X86/X86ISelLowering.cpp | 31 ++- test/CodeGen/WinEH/cppeh-catch-unwind.ll | 6 - test/CodeGen/WinEH/cppeh-prepared-catch.ll | 206 ++++++++++++++++++ ...cleanups.ll => cppeh-prepared-cleanups.ll} | 8 +- 14 files changed, 385 insertions(+), 91 deletions(-) create mode 100644 test/CodeGen/WinEH/cppeh-prepared-catch.ll rename test/CodeGen/WinEH/{cppeh-cleanups.ll => cppeh-prepared-cleanups.ll} (99%) diff --git a/docs/ExceptionHandling.rst b/docs/ExceptionHandling.rst index 0c12b282282..21de19b0752 100644 --- a/docs/ExceptionHandling.rst +++ b/docs/ExceptionHandling.rst @@ -551,17 +551,6 @@ This object is used by Windows native exception handling on non-x86 platforms where xdata unwind information is used. It is typically an 8 byte chunk of memory treated as two 32-bit integers. -``llvm.eh.parentframe`` ------------------------ - -.. code-block:: llvm - - void @llvm.eh.parentframe(i8*) - -This intrinsic designates the provided static alloca as the object which holds -the address of the parent frame. -This object is used by Windows native exception handling on non-x86 platforms -where xdata unwind information is used. SJLJ Intrinsics --------------- diff --git a/include/llvm/CodeGen/WinEHFuncInfo.h b/include/llvm/CodeGen/WinEHFuncInfo.h index 807afe6f8ae..8d9c6f24d5f 100644 --- a/include/llvm/CodeGen/WinEHFuncInfo.h +++ b/include/llvm/CodeGen/WinEHFuncInfo.h @@ -103,7 +103,9 @@ struct WinEHUnwindMapEntry { struct WinEHHandlerType { int Adjectives; - GlobalValue *TypeDescriptor; + GlobalVariable *TypeDescriptor; + int CatchObjIdx; + int CatchObjOffset; Function *Handler; }; @@ -111,7 +113,7 @@ struct WinEHTryBlockMapEntry { int TryLow; int TryHigh; int CatchHigh; - SmallVector HandlerArray; + SmallVector HandlerArray; }; struct WinEHFuncInfo { diff --git a/include/llvm/IR/Intrinsics.td b/include/llvm/IR/Intrinsics.td index c5ab91ec9b5..da9d8cb61f5 100644 --- a/include/llvm/IR/Intrinsics.td +++ b/include/llvm/IR/Intrinsics.td @@ -425,10 +425,6 @@ def int_eh_actions : Intrinsic<[llvm_ptr_ty], [llvm_vararg_ty], []>; // for WinEH. def int_eh_unwindhelp : Intrinsic<[], [llvm_ptr_ty], []>; -// Designates the provided static alloca as the object which holds the address -// of the parent frame. Required for WinEH. -def int_eh_parentframe : Intrinsic<[], [llvm_ptrptr_ty], []>; - // __builtin_unwind_init is an undocumented GCC intrinsic that causes all // callee-saved registers to be saved and restored (regardless of whether they // are used) in the calling function. It is used by libgcc_eh. diff --git a/include/llvm/MC/MCContext.h b/include/llvm/MC/MCContext.h index c1f2fac82f7..63faeceec9d 100644 --- a/include/llvm/MC/MCContext.h +++ b/include/llvm/MC/MCContext.h @@ -259,6 +259,7 @@ namespace llvm { MCSymbol *getOrCreateSectionSymbol(const MCSectionELF &Section); MCSymbol *getOrCreateFrameAllocSymbol(StringRef FuncName, unsigned Idx); + MCSymbol *getOrCreateParentFrameOffsetSymbol(StringRef FuncName); /// Get the symbol for \p Name, or null. MCSymbol *LookupSymbol(const Twine &Name) const; diff --git a/lib/CodeGen/AsmPrinter/Win64Exception.cpp b/lib/CodeGen/AsmPrinter/Win64Exception.cpp index 974e94dcb6a..7a82daa3337 100644 --- a/lib/CodeGen/AsmPrinter/Win64Exception.cpp +++ b/lib/CodeGen/AsmPrinter/Win64Exception.cpp @@ -68,6 +68,27 @@ void Win64Exception::beginFunction(const MachineFunction *MF) { shouldEmitLSDA = shouldEmitPersonality && LSDAEncoding != dwarf::DW_EH_PE_omit; + + // If this was an outlined handler, we need to define the label corresponding + // to the offset of the parent frame relative to the stack pointer after the + // prologue. + const Function *F = MF->getFunction(); + const Function *ParentF = MMI->getWinEHParent(F); + if (F != ParentF) { + WinEHFuncInfo &FuncInfo = MMI->getWinEHFuncInfo(ParentF); + auto I = FuncInfo.CatchHandlerParentFrameObjOffset.find(F); + if (I != FuncInfo.CatchHandlerParentFrameObjOffset.end()) { + MCSymbol *HandlerTypeParentFrameOffset = + Asm->OutContext.getOrCreateParentFrameOffsetSymbol( + GlobalValue::getRealLinkageName(F->getName())); + + // Emit a symbol assignment. + Asm->OutStreamer.EmitAssignment( + HandlerTypeParentFrameOffset, + MCConstantExpr::Create(I->second, Asm->OutContext)); + } + } + if (!shouldEmitPersonality && !shouldEmitMoves) return; @@ -253,6 +274,7 @@ void Win64Exception::emitCXXFrameHandler3Table(const MachineFunction *MF) { const Function *F = MF->getFunction(); const Function *ParentF = MMI->getWinEHParent(F); auto &OS = Asm->OutStreamer; + WinEHFuncInfo &FuncInfo = MMI->getWinEHFuncInfo(ParentF); StringRef ParentLinkageName = GlobalValue::getRealLinkageName(ParentF->getName()); @@ -279,8 +301,6 @@ void Win64Exception::emitCXXFrameHandler3Table(const MachineFunction *MF) { // an ordinary call) between the end of the previous try-range and now. bool SawPotentiallyThrowing = false; - WinEHFuncInfo &FuncInfo = MMI->getWinEHFuncInfo(ParentF); - int LastEHState = -2; // The parent function and the catch handlers contribute to the 'ip2state' @@ -424,11 +444,17 @@ void Win64Exception::emitCXXFrameHandler3Table(const MachineFunction *MF) { // }; OS.EmitLabel(HandlerMapXData); for (const WinEHHandlerType &HT : TBME.HandlerArray) { + MCSymbol *ParentFrameOffset = + Asm->OutContext.getOrCreateParentFrameOffsetSymbol( + GlobalValue::getRealLinkageName(HT.Handler->getName())); + const MCSymbolRefExpr *ParentFrameOffsetRef = MCSymbolRefExpr::Create( + ParentFrameOffset, MCSymbolRefExpr::VK_None, Asm->OutContext); + OS.EmitIntValue(HT.Adjectives, 4); // Adjectives OS.EmitValue(createImageRel32(HT.TypeDescriptor), 4); // Type - OS.EmitIntValue(0, 4); // CatchObjOffset + OS.EmitIntValue(HT.CatchObjOffset, 4); // CatchObjOffset OS.EmitValue(createImageRel32(HT.Handler), 4); // Handler - OS.EmitIntValue(0, 4); // ParentFrameOffset + OS.EmitValue(ParentFrameOffsetRef, 4); // ParentFrameOffset } } } diff --git a/lib/CodeGen/SelectionDAG/FunctionLoweringInfo.cpp b/lib/CodeGen/SelectionDAG/FunctionLoweringInfo.cpp index c00b11893bf..e2235fbc044 100644 --- a/lib/CodeGen/SelectionDAG/FunctionLoweringInfo.cpp +++ b/lib/CodeGen/SelectionDAG/FunctionLoweringInfo.cpp @@ -88,6 +88,7 @@ struct WinEHNumbering { int NextState; SmallVector HandlerStack; + SmallPtrSet VisitedHandlers; int currentEHNumber() const { return HandlerStack.empty() ? -1 : HandlerStack.back()->getEHState(); @@ -96,7 +97,9 @@ struct WinEHNumbering { void parseEHActions(const IntrinsicInst *II, SmallVectorImpl &Actions); void createUnwindMapEntry(int ToState, ActionHandler *AH); - void proccessCallSite(ArrayRef Actions, ImmutableCallSite CS); + void createTryBlockMapEntry(int TryLow, int TryHigh, + ArrayRef Handlers); + void processCallSite(ArrayRef Actions, ImmutableCallSite CS); void calculateStateNumbers(const Function &F); }; } @@ -276,8 +279,12 @@ void FunctionLoweringInfo::set(const Function &fn, MachineFunction &mf, MBBMap[Invoke->getSuccessor(1)]->setIsLandingPad(); // Calculate EH numbers for WinEH. - if (fn.getFnAttribute("wineh-parent").getValueAsString() == fn.getName()) - WinEHNumbering(MMI.getWinEHFuncInfo(&fn)).calculateStateNumbers(fn); + if (fn.getFnAttribute("wineh-parent").getValueAsString() == fn.getName()) { + WinEHNumbering Num(MMI.getWinEHFuncInfo(&fn)); + Num.calculateStateNumbers(fn); + // Pop everything on the handler stack. + Num.processCallSite(None, ImmutableCallSite()); + } } void WinEHNumbering::parseEHActions(const IntrinsicInst *II, @@ -309,13 +316,42 @@ void WinEHNumbering::parseEHActions(const IntrinsicInst *II, void WinEHNumbering::createUnwindMapEntry(int ToState, ActionHandler *AH) { WinEHUnwindMapEntry UME; UME.ToState = ToState; - if (auto *CH = dyn_cast(AH)) + if (auto *CH = dyn_cast_or_null(AH)) UME.Cleanup = cast(CH->getHandlerBlockOrFunc()); else UME.Cleanup = nullptr; FuncInfo.UnwindMap.push_back(UME); } +void WinEHNumbering::createTryBlockMapEntry(int TryLow, int TryHigh, + ArrayRef Handlers) { + WinEHTryBlockMapEntry TBME; + TBME.TryLow = TryLow; + TBME.TryHigh = TryHigh; + // FIXME: This should be revisited when we want to throw inside a catch + // handler. + TBME.CatchHigh = INT_MAX; + assert(TBME.TryLow <= TBME.TryHigh); + assert(TBME.CatchHigh > TBME.TryHigh); + for (CatchHandler *CH : Handlers) { + WinEHHandlerType HT; + auto *GV = cast(CH->getSelector()->stripPointerCasts()); + // Selectors are always pointers to GlobalVariables with 'struct' type. + // The struct has two fields, adjectives and a type descriptor. + auto *CS = cast(GV->getInitializer()); + HT.Adjectives = + cast(CS->getAggregateElement(0U))->getZExtValue(); + HT.TypeDescriptor = cast( + CS->getAggregateElement(1)->stripPointerCasts()); + HT.Handler = cast(CH->getHandlerBlockOrFunc()); + // FIXME: We don't support catching objects yet! + HT.CatchObjIdx = INT_MAX; + HT.CatchObjOffset = 0; + TBME.HandlerArray.push_back(HT); + } + FuncInfo.TryBlockMap.push_back(TBME); +} + static void print_name(const Value *V) { #ifndef NDEBUG if (!V) { @@ -330,10 +366,8 @@ static void print_name(const Value *V) { #endif } -void WinEHNumbering::proccessCallSite(ArrayRef Actions, - ImmutableCallSite CS) { - // float, int - // float, double, int +void WinEHNumbering::processCallSite(ArrayRef Actions, + ImmutableCallSite CS) { int FirstMismatch = 0; for (int E = std::min(HandlerStack.size(), Actions.size()); FirstMismatch < E; ++FirstMismatch) { @@ -343,28 +377,65 @@ void WinEHNumbering::proccessCallSite(ArrayRef Actions, delete Actions[FirstMismatch]; } + bool EnteringScope = (int)Actions.size() > FirstMismatch; + bool ExitingScope = (int)HandlerStack.size() > FirstMismatch; + // Don't recurse while we are looping over the handler stack. Instead, defer // the numbering of the catch handlers until we are done popping. - SmallVector UnnumberedHandlers; + SmallVector PoppedCatches; for (int I = HandlerStack.size() - 1; I >= FirstMismatch; --I) { - if (auto *CH = dyn_cast(HandlerStack.back())) - if (const auto *F = dyn_cast(CH->getHandlerBlockOrFunc())) - UnnumberedHandlers.push_back(F); - // Pop the handlers off of the stack. - delete HandlerStack.back(); + if (auto *CH = dyn_cast(HandlerStack.back())) { + PoppedCatches.push_back(CH); + } else { + // Delete cleanup handlers + delete HandlerStack.back(); + } HandlerStack.pop_back(); } - for (const Function *F : UnnumberedHandlers) - calculateStateNumbers(*F); + // We need to create a new state number if we are exiting a try scope and we + // will not push any more actions. + int TryHigh = NextState - 1; + if (ExitingScope && !EnteringScope && !PoppedCatches.empty()) { + createUnwindMapEntry(currentEHNumber(), nullptr); + ++NextState; + } + + int LastTryLowIdx = 0; + for (int I = 0, E = PoppedCatches.size(); I != E; ++I) { + CatchHandler *CH = PoppedCatches[I]; + if (I + 1 == E || CH->getEHState() != PoppedCatches[I + 1]->getEHState()) { + int TryLow = CH->getEHState(); + auto Handlers = + makeArrayRef(&PoppedCatches[LastTryLowIdx], I - LastTryLowIdx + 1); + createTryBlockMapEntry(TryLow, TryHigh, Handlers); + LastTryLowIdx = I + 1; + } + } + for (CatchHandler *CH : PoppedCatches) { + if (auto *F = dyn_cast(CH->getHandlerBlockOrFunc())) + calculateStateNumbers(*F); + delete CH; + } + + bool LastActionWasCatch = false; for (size_t I = FirstMismatch; I != Actions.size(); ++I) { - createUnwindMapEntry(currentEHNumber(), Actions[I]); - Actions[I]->setEHState(NextState++); - DEBUG(dbgs() << "Creating unwind map entry for: ("); - print_name(Actions[I]->getHandlerBlockOrFunc()); - DEBUG(dbgs() << ", " << currentEHNumber() << ")\n"); + // We can reuse eh states when pushing two catches for the same invoke. + bool CurrActionIsCatch = isa(Actions[I]); + // FIXME: Reenable this optimization! + if (CurrActionIsCatch && LastActionWasCatch && false) { + Actions[I]->setEHState(currentEHNumber()); + } else { + createUnwindMapEntry(currentEHNumber(), Actions[I]); + Actions[I]->setEHState(NextState); + NextState++; + DEBUG(dbgs() << "Creating unwind map entry for: ("); + print_name(Actions[I]->getHandlerBlockOrFunc()); + DEBUG(dbgs() << ", " << currentEHNumber() << ")\n"); + } HandlerStack.push_back(Actions[I]); + LastActionWasCatch = CurrActionIsCatch; } DEBUG(dbgs() << "In EHState " << currentEHNumber() << " for CallSite: "); @@ -373,6 +444,10 @@ void WinEHNumbering::proccessCallSite(ArrayRef Actions, } void WinEHNumbering::calculateStateNumbers(const Function &F) { + auto I = VisitedHandlers.insert(&F); + if (!I.second) + return; // We've already visited this handler, don't renumber it. + DEBUG(dbgs() << "Calculating state numbers for: " << F.getName() << '\n'); SmallVector ActionList; for (const BasicBlock &BB : F) { @@ -380,21 +455,21 @@ void WinEHNumbering::calculateStateNumbers(const Function &F) { const auto *CI = dyn_cast(&I); if (!CI || CI->doesNotThrow()) continue; - proccessCallSite(None, CI); + processCallSite(None, CI); } const auto *II = dyn_cast(BB.getTerminator()); if (!II) continue; const LandingPadInst *LPI = II->getLandingPadInst(); - if (auto *ActionsCall = dyn_cast(LPI->getNextNode())) { - assert(ActionsCall->getIntrinsicID() == Intrinsic::eh_actions); - parseEHActions(ActionsCall, ActionList); - proccessCallSite(ActionList, II); - ActionList.clear(); - FuncInfo.LandingPadStateMap[LPI] = currentEHNumber(); - } + auto *ActionsCall = dyn_cast(LPI->getNextNode()); + if (!ActionsCall) + continue; + assert(ActionsCall->getIntrinsicID() == Intrinsic::eh_actions); + parseEHActions(ActionsCall, ActionList); + processCallSite(ActionList, II); + ActionList.clear(); + FuncInfo.LandingPadStateMap[LPI] = currentEHNumber(); } - proccessCallSite(None, ImmutableCallSite()); } /// clear - Clear out all the function-specific state. This returns this diff --git a/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp index fd9d7e6b69a..fa58614a4b3 100644 --- a/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ b/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -5450,18 +5450,6 @@ SelectionDAGBuilder::visitIntrinsicCall(const CallInst &I, unsigned Intrinsic) { case Intrinsic::eh_begincatch: case Intrinsic::eh_endcatch: llvm_unreachable("begin/end catch intrinsics not lowered in codegen"); - case Intrinsic::eh_parentframe: { - AllocaInst *Slot = - cast(I.getArgOperand(0)->stripPointerCasts()); - assert(FuncInfo.StaticAllocaMap.count(Slot) && - "can only use static allocas with llvm.eh.parentframe"); - int FI = FuncInfo.StaticAllocaMap[Slot]; - MachineFunction &MF = DAG.getMachineFunction(); - const Function *F = MF.getFunction(); - MachineModuleInfo &MMI = MF.getMMI(); - MMI.getWinEHFuncInfo(F).CatchHandlerParentFrameObjIdx[F] = FI; - return nullptr; - } case Intrinsic::eh_unwindhelp: { AllocaInst *Slot = cast(I.getArgOperand(0)->stripPointerCasts()); diff --git a/lib/CodeGen/WinEHPrepare.cpp b/lib/CodeGen/WinEHPrepare.cpp index 94e046c424d..5b4ee17d949 100644 --- a/lib/CodeGen/WinEHPrepare.cpp +++ b/lib/CodeGen/WinEHPrepare.cpp @@ -638,16 +638,6 @@ bool WinEHPrepare::outlineHandler(ActionHandler *Action, Function *SrcFn, if (!LPadMap.isInitialized()) LPadMap.mapLandingPad(LPad); if (auto *CatchAction = dyn_cast(Action)) { - // Insert an alloca for the object which holds the address of the parent's - // frame pointer. The stack offset of this object needs to be encoded in - // xdata. - AllocaInst *ParentFrame = new AllocaInst(Int8PtrType, "parentframe", Entry); - Builder.CreateStore(&Handler->getArgumentList().back(), ParentFrame, - /*isStore=*/true); - Function *ParentFrameFn = - Intrinsic::getDeclaration(M, Intrinsic::eh_parentframe); - Builder.CreateCall(ParentFrameFn, ParentFrame); - Constant *Sel = CatchAction->getSelector(); Director.reset(new WinEHCatchDirector(Handler, Sel, VarInfo, LPadMap)); LPadMap.remapSelector(VMap, ConstantInt::get(Type::getInt32Ty(Context), 1)); diff --git a/lib/IR/Verifier.cpp b/lib/IR/Verifier.cpp index 15fbea33d89..1cdb99c4366 100644 --- a/lib/IR/Verifier.cpp +++ b/lib/IR/Verifier.cpp @@ -3228,13 +3228,6 @@ void Verifier::visitIntrinsicFunctionCall(Intrinsic::ID ID, CallInst &CI) { break; } - case Intrinsic::eh_parentframe: { - auto *AI = dyn_cast(CI.getArgOperand(0)->stripPointerCasts()); - Assert(AI && AI->isStaticAlloca(), - "llvm.eh.parentframe requires a static alloca", &CI); - break; - } - case Intrinsic::eh_unwindhelp: { auto *AI = dyn_cast(CI.getArgOperand(0)->stripPointerCasts()); Assert(AI && AI->isStaticAlloca(), diff --git a/lib/MC/MCContext.cpp b/lib/MC/MCContext.cpp index 2cb81d06b05..6e7a3bc4db7 100644 --- a/lib/MC/MCContext.cpp +++ b/lib/MC/MCContext.cpp @@ -139,6 +139,11 @@ MCSymbol *MCContext::getOrCreateFrameAllocSymbol(StringRef FuncName, "$frame_escape_" + Twine(Idx)); } +MCSymbol *MCContext::getOrCreateParentFrameOffsetSymbol(StringRef FuncName) { + return GetOrCreateSymbol(Twine(MAI->getPrivateGlobalPrefix()) + FuncName + + "$parent_frame_offset"); +} + MCSymbol *MCContext::CreateSymbol(StringRef Name, bool AlwaysAddSuffix) { // Determine whether this is an assembler temporary or normal label, if used. bool IsTemporary = false; diff --git a/lib/Target/X86/X86ISelLowering.cpp b/lib/Target/X86/X86ISelLowering.cpp index 84b71429f33..966aec0df96 100644 --- a/lib/Target/X86/X86ISelLowering.cpp +++ b/lib/Target/X86/X86ISelLowering.cpp @@ -32,6 +32,7 @@ #include "llvm/CodeGen/MachineJumpTableInfo.h" #include "llvm/CodeGen/MachineModuleInfo.h" #include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/CodeGen/WinEHFuncInfo.h" #include "llvm/IR/CallSite.h" #include "llvm/IR/CallingConv.h" #include "llvm/IR/Constants.h" @@ -2266,6 +2267,12 @@ static ArrayRef get64BitArgumentXMMs(MachineFunction &MF, return makeArrayRef(std::begin(XMMArgRegs64Bit), std::end(XMMArgRegs64Bit)); } +static bool isOutlinedHandler(const MachineFunction &MF) { + const MachineModuleInfo &MMI = MF.getMMI(); + const Function *F = MF.getFunction(); + return MMI.getWinEHParent(F) != F; +} + SDValue X86TargetLowering::LowerFormalArguments(SDValue Chain, CallingConv::ID CallConv, @@ -2277,6 +2284,7 @@ X86TargetLowering::LowerFormalArguments(SDValue Chain, const { MachineFunction &MF = DAG.getMachineFunction(); X86MachineFunctionInfo *FuncInfo = MF.getInfo(); + const TargetFrameLowering &TFI = *Subtarget->getFrameLowering(); const Function* Fn = MF.getFunction(); if (Fn->hasExternalLinkage() && @@ -2452,7 +2460,6 @@ X86TargetLowering::LowerFormalArguments(SDValue Chain, } if (IsWin64) { - const TargetFrameLowering &TFI = *Subtarget->getFrameLowering(); // Get to the caller-allocated home save location. Add 8 to account // for the return address. int HomeOffset = TFI.getOffsetOfLocalArea() + 8; @@ -2505,6 +2512,28 @@ X86TargetLowering::LowerFormalArguments(SDValue Chain, if (!MemOps.empty()) Chain = DAG.getNode(ISD::TokenFactor, dl, MVT::Other, MemOps); + } else if (IsWin64 && isOutlinedHandler(MF)) { + // Get to the caller-allocated home save location. Add 8 to account + // for the return address. + int HomeOffset = TFI.getOffsetOfLocalArea() + 8; + FuncInfo->setRegSaveFrameIndex(MFI->CreateFixedObject( + /*Size=*/1, /*SPOffset=*/HomeOffset + 8, /*Immutable=*/false)); + + MachineModuleInfo &MMI = MF.getMMI(); + MMI.getWinEHFuncInfo(Fn) + .CatchHandlerParentFrameObjIdx[const_cast(Fn)] = + FuncInfo->getRegSaveFrameIndex(); + + // Store the second integer parameter (rdx) into rsp+16 relative to the + // stack pointer at the entry of the function. + SDValue RSFIN = + DAG.getFrameIndex(FuncInfo->getRegSaveFrameIndex(), getPointerTy()); + unsigned GPR = MF.addLiveIn(X86::RDX, &X86::GR64RegClass); + SDValue Val = DAG.getCopyFromReg(Chain, dl, GPR, MVT::i64); + Chain = DAG.getStore( + Val.getValue(1), dl, Val, RSFIN, + MachinePointerInfo::getFixedStack(FuncInfo->getRegSaveFrameIndex()), + /*isVolatile=*/true, /*isNonTemporal=*/false, /*Alignment=*/0); } if (isVarArg && MFI->hasMustTailInVarArgFunc()) { diff --git a/test/CodeGen/WinEH/cppeh-catch-unwind.ll b/test/CodeGen/WinEH/cppeh-catch-unwind.ll index 60a5256a133..33d85517efa 100644 --- a/test/CodeGen/WinEH/cppeh-catch-unwind.ll +++ b/test/CodeGen/WinEH/cppeh-catch-unwind.ll @@ -180,9 +180,6 @@ eh.resume: ; preds = %catch.dispatch7 ; CHECK-LABEL: define internal i8* @"\01?test@@YAXXZ.catch"(i8*, i8*) ; CHECK: entry: -; CHECK: [[PARENTFRAME:\%.+]] = alloca i8* -; CHECK: store volatile i8* %1, i8** [[PARENTFRAME]] -; CHECK: call void @llvm.eh.parentframe(i8** [[PARENTFRAME]]) ; CHECK: [[RECOVER_TMP1:\%.+]] = call i8* @llvm.framerecover(i8* bitcast (void ()* @"\01?test@@YAXXZ" to i8*), i8* %1, i32 0) ; CHECK: [[TMP1_PTR:\%.+]] = bitcast i8* [[RECOVER_TMP1]] to i32* ; CHECK: call void @"\01?handle_exception@@YAXXZ"() @@ -199,9 +196,6 @@ eh.resume: ; preds = %catch.dispatch7 ; CHECK-LABEL: define internal i8* @"\01?test@@YAXXZ.catch1"(i8*, i8*) ; CHECK: entry: -; CHECK: [[PARENTFRAME:\%.+]] = alloca i8* -; CHECK: store volatile i8* %1, i8** [[PARENTFRAME]] -; CHECK: call void @llvm.eh.parentframe(i8** [[PARENTFRAME]]) ; CHECK: [[RECOVER_TMP0:\%.+]] = call i8* @llvm.framerecover(i8* bitcast (void ()* @"\01?test@@YAXXZ" to i8*), i8* %1, i32 2) ; CHECK: [[TMP0_PTR:\%.+]] = bitcast i8* [[RECOVER_TMP0]] to i32* ; CHECK: invoke void @"\01?handle_exception@@YAXXZ"() diff --git a/test/CodeGen/WinEH/cppeh-prepared-catch.ll b/test/CodeGen/WinEH/cppeh-prepared-catch.ll new file mode 100644 index 00000000000..a294a075835 --- /dev/null +++ b/test/CodeGen/WinEH/cppeh-prepared-catch.ll @@ -0,0 +1,206 @@ +; RUN: llc < %s | FileCheck %s + +target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc" + +; This test case is equivalent to: +; void f() { +; try { +; try { +; may_throw(); +; } catch (int &) { +; may_throw(); +; } +; may_throw(); +; } catch (double) { +; } +; } + + +%rtti.TypeDescriptor2 = type { i8**, i8*, [3 x i8] } +%eh.CatchHandlerType = type { i32, i8* } + +$"\01??_R0N@8" = comdat any + +$"\01??_R0H@8" = comdat any + +@"\01??_7type_info@@6B@" = external constant i8* +@"\01??_R0N@8" = linkonce_odr global %rtti.TypeDescriptor2 { i8** @"\01??_7type_info@@6B@", i8* null, [3 x i8] c".N\00" }, comdat +@llvm.eh.handlertype.N.0 = private unnamed_addr constant %eh.CatchHandlerType { i32 0, i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0N@8" to i8*) }, section "llvm.metadata" +@"\01??_R0H@8" = linkonce_odr global %rtti.TypeDescriptor2 { i8** @"\01??_7type_info@@6B@", i8* null, [3 x i8] c".H\00" }, comdat +@llvm.eh.handlertype.H.8 = private unnamed_addr constant %eh.CatchHandlerType { i32 8, i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0H@8" to i8*) }, section "llvm.metadata" + +define internal i8* @"\01?f@@YAXXZ.catch"(i8*, i8*) #4 { +entry: + %.i8 = call i8* @llvm.framerecover(i8* bitcast (void ()* @"\01?f@@YAXXZ" to i8*), i8* %1, i32 0) + %2 = bitcast i8* %.i8 to i32** + %3 = bitcast i32** %2 to i8* + invoke void @"\01?may_throw@@YAXXZ"() + to label %invoke.cont2 unwind label %lpad1 + +invoke.cont2: ; preds = %entry + ret i8* blockaddress(@"\01?f@@YAXXZ", %try.cont) + +lpad1: ; preds = %entry + %4 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + cleanup + catch %eh.CatchHandlerType* @llvm.eh.handlertype.N.0 + %recover = call i8* (...)* @llvm.eh.actions(i32 1, i8* bitcast (%eh.CatchHandlerType* @llvm.eh.handlertype.N.0 to i8*), double* null, i8* (i8*, i8*)* @"\01?f@@YAXXZ.catch1") + indirectbr i8* %recover, [label %invoke.cont2] +} + +; CHECK-LABEL: "?f@@YAXXZ.catch": +; CHECK: ".L?f@@YAXXZ.catch$parent_frame_offset" = 56 +; CHECK: movq %rdx, 56(%rsp) +; CHECK: .seh_handlerdata +; CHECK: .long ("$cppxdata$?f@@YAXXZ")@IMGREL + + +define internal i8* @"\01?f@@YAXXZ.catch1"(i8*, i8*) #4 { +entry: + %.i8 = call i8* @llvm.framerecover(i8* bitcast (void ()* @"\01?f@@YAXXZ" to i8*), i8* %1, i32 1) + %2 = bitcast i8* %.i8 to double* + %3 = bitcast double* %2 to i8* + invoke void (...)* @llvm.donothing() + to label %done unwind label %lpad + +done: + ret i8* blockaddress(@"\01?f@@YAXXZ", %try.cont8) + +lpad: ; preds = %entry + %4 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + cleanup + unreachable +} + +; CHECK-LABEL: "?f@@YAXXZ.catch1": +; CHECK: ".L?f@@YAXXZ.catch1$parent_frame_offset" = 16 +; CHECK: movq %rdx, 16(%rsp) +; CHECK: .seh_handlerdata +; CHECK: .long ("$cppxdata$?f@@YAXXZ")@IMGREL + +define void @"\01?f@@YAXXZ"() #0 { +entry: + %unwindhelp = alloca i64 + %exn.slot = alloca i8* + %ehselector.slot = alloca i32 + %0 = alloca i32*, align 8 + %1 = alloca double, align 8 + call void (...)* @llvm.frameescape(i32** %0, double* %1) + store volatile i64 -2, i64* %unwindhelp + %2 = bitcast i64* %unwindhelp to i8* + call void @llvm.eh.unwindhelp(i8* %2) + invoke void @"\01?may_throw@@YAXXZ"() + to label %invoke.cont unwind label %lpad2 + +invoke.cont: ; preds = %entry + br label %try.cont + +lpad2: ; preds = %entry + %3 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + catch %eh.CatchHandlerType* @llvm.eh.handlertype.H.8 + catch %eh.CatchHandlerType* @llvm.eh.handlertype.N.0 + %recover = call i8* (...)* @llvm.eh.actions(i32 1, i8* bitcast (%eh.CatchHandlerType* @llvm.eh.handlertype.H.8 to i8*), i32** %0, i8* (i8*, i8*)* @"\01?f@@YAXXZ.catch", i32 1, i8* bitcast (%eh.CatchHandlerType* @llvm.eh.handlertype.N.0 to i8*), double* %1, i8* (i8*, i8*)* @"\01?f@@YAXXZ.catch1") + indirectbr i8* %recover, [label %try.cont, label %try.cont8] + +try.cont: ; preds = %lpad2, %invoke.cont + invoke void @"\01?may_throw@@YAXXZ"() + to label %try.cont8 unwind label %lpad1 + +lpad1: + %4 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) + catch %eh.CatchHandlerType* @llvm.eh.handlertype.N.0 + %recover2 = call i8* (...)* @llvm.eh.actions(i32 1, i8* bitcast (%eh.CatchHandlerType* @llvm.eh.handlertype.N.0 to i8*), double* null, i8* (i8*, i8*)* @"\01?f@@YAXXZ.catch1") + indirectbr i8* %recover2, [label %try.cont8] + +try.cont8: ; preds = %lpad2, %try.cont + ret void +} + +; CHECK-LABEL: "?f@@YAXXZ": +; CHECK: .seh_handlerdata +; CHECK-NEXT: .long ("$cppxdata$?f@@YAXXZ")@IMGREL +; CHECK-NEXT:"$cppxdata$?f@@YAXXZ": +; CHECK-NEXT: .long 429065506 +; CHECK-NEXT: .long 4 +; CHECK-NEXT: .long ("$stateUnwindMap$?f@@YAXXZ")@IMGREL +; CHECK-NEXT: .long 2 +; CHECK-NEXT: .long ("$tryMap$?f@@YAXXZ")@IMGREL +; CHECK-NEXT: .long 3 +; CHECK-NEXT: .long ("$ip2state$?f@@YAXXZ")@IMGREL +; CHECK-NEXT: .long 64 +; CHECK-NEXT: .long 0 +; CHECK-NEXT: .long 1 +; CHECK-NEXT:"$stateUnwindMap$?f@@YAXXZ": +; CHECK-NEXT: .long -1 +; CHECK-NEXT: .long 0 +; CHECK-NEXT: .long 0 +; CHECK-NEXT: .long 0 +; CHECK-NEXT: .long 0 +; CHECK-NEXT: .long 0 +; CHECK-NEXT: .long -1 +; CHECK-NEXT: .long 0 +; CHECK-NEXT:"$tryMap$?f@@YAXXZ": +; CHECK-NEXT: .long 1 +; CHECK-NEXT: .long 1 +; CHECK-NEXT: .long 2147483647 +; CHECK-NEXT: .long 1 +; CHECK-NEXT: .long ("$handlerMap$0$?f@@YAXXZ")@IMGREL +; CHECK-NEXT: .long 0 +; CHECK-NEXT: .long 2 +; CHECK-NEXT: .long 2147483647 +; CHECK-NEXT: .long 1 +; CHECK-NEXT: .long ("$handlerMap$1$?f@@YAXXZ")@IMGREL +; CHECK-NEXT:"$handlerMap$0$?f@@YAXXZ": +; CHECK-NEXT: .long 8 +; CHECK-NEXT: .long "??_R0H@8"@IMGREL +; CHECK-NEXT: .long 0 +; CHECK-NEXT: .long "?f@@YAXXZ.catch"@IMGREL +; CHECK-NEXT: .long ".L?f@@YAXXZ.catch$parent_frame_offset" +; CHECK-NEXT:"$handlerMap$1$?f@@YAXXZ": +; CHECK-NEXT: .long 0 +; CHECK-NEXT: .long "??_R0N@8"@IMGREL +; CHECK-NEXT: .long 0 +; CHECK-NEXT: .long "?f@@YAXXZ.catch1"@IMGREL +; CHECK-NEXT: .long ".L?f@@YAXXZ.catch1$parent_frame_offset" +; CHECK-NEXT:"$ip2state$?f@@YAXXZ": +; CHECK-NEXT: .long .Ltmp0@IMGREL +; CHECK-NEXT: .long 0 +; CHECK-NEXT: .long .Ltmp13@IMGREL +; CHECK-NEXT: .long 1 +; CHECK-NEXT: .long .Ltmp16@IMGREL +; CHECK-NEXT: .long 0 + + +declare void @"\01?may_throw@@YAXXZ"() #1 + +declare i32 @__CxxFrameHandler3(...) + +; Function Attrs: nounwind readnone +declare i32 @llvm.eh.typeid.for(i8*) #2 + +; Function Attrs: nounwind +declare void @llvm.eh.begincatch(i8* nocapture, i8* nocapture) #3 + +; Function Attrs: nounwind +declare void @llvm.eh.endcatch() #3 + +; Function Attrs: nounwind +declare i8* @llvm.eh.actions(...) #3 + +; Function Attrs: nounwind +declare void @llvm.frameescape(...) #3 + +; Function Attrs: nounwind readnone +declare i8* @llvm.framerecover(i8*, i8*, i32) #2 + +; Function Attrs: nounwind +declare void @llvm.eh.unwindhelp(i8*) #3 + +declare void @llvm.donothing(...) + +attributes #0 = { "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" "wineh-parent"="?f@@YAXXZ" } +attributes #1 = { "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 #2 = { nounwind readnone } +attributes #3 = { nounwind } +attributes #4 = { "wineh-parent"="?f@@YAXXZ" } diff --git a/test/CodeGen/WinEH/cppeh-cleanups.ll b/test/CodeGen/WinEH/cppeh-prepared-cleanups.ll similarity index 99% rename from test/CodeGen/WinEH/cppeh-cleanups.ll rename to test/CodeGen/WinEH/cppeh-prepared-cleanups.ll index 64d9980be39..0a76a22208c 100644 --- a/test/CodeGen/WinEH/cppeh-cleanups.ll +++ b/test/CodeGen/WinEH/cppeh-prepared-cleanups.ll @@ -27,7 +27,7 @@ $_TI1H = comdat any @_TI1H = linkonce_odr unnamed_addr constant %eh.ThrowInfo { i32 0, i32 0, i32 0, i32 trunc (i64 sub nuw nsw (i64 ptrtoint (%eh.CatchableTypeArray.1* @_CTA1H to i64), i64 ptrtoint (i8* @__ImageBase to i64)) to i32) }, section ".xdata", comdat -; CHECK-LABEL: ?test1@@YAXXZ": +; CHECK-LABEL: "?test1@@YAXXZ": ; CHECK: .seh_handlerdata ; CHECK-NEXT: .long ("$cppxdata$?test1@@YAXXZ")@IMGREL ; CHECK-NEXT:"$cppxdata$?test1@@YAXXZ": @@ -126,11 +126,11 @@ entry: %s1 = alloca %struct.S, align 1 %frombool = zext i1 %b to i8 store i8 %frombool, i8* %b.addr, align 1 - call void @"\01?may_throw@@YAXXZ"() - call void (...)* @llvm.frameescape(%struct.S* %s, %struct.S* %s1) - store volatile i64 -2, i64* %unwindhelp %0 = bitcast i64* %unwindhelp to i8* + store volatile i64 -2, i64* %unwindhelp + call void (...)* @llvm.frameescape(%struct.S* %s, %struct.S* %s1) call void @llvm.eh.unwindhelp(i8* %0) + call void @"\01?may_throw@@YAXXZ"() invoke void @"\01?may_throw@@YAXXZ"() to label %invoke.cont unwind label %lpad1 -- 2.34.1