Index: llvm/trunk/include/llvm/IR/IntrinsicsX86.td =================================================================== --- llvm/trunk/include/llvm/IR/IntrinsicsX86.td +++ llvm/trunk/include/llvm/IR/IntrinsicsX86.td @@ -21,9 +21,17 @@ // SEH intrinsics for Windows let TargetPrefix = "x86" in { def int_x86_seh_lsda : Intrinsic<[llvm_ptr_ty], [llvm_ptr_ty], [IntrNoMem]>; - def int_x86_seh_exceptioninfo : Intrinsic<[llvm_ptr_ty], - [llvm_ptr_ty, llvm_ptr_ty], - [IntrReadMem]>; + + // Restores the frame, base, and stack pointers as necessary after recovering + // from an exception. Any block resuming control flow in the parent function + // should call this before accessing any stack memory. + def int_x86_seh_restoreframe : Intrinsic<[], [], []>; + + // Given a pointer to the end of an EH registration object, returns the true + // parent frame address that can be used with llvm.framerecover. + def int_x86_seh_recoverfp : Intrinsic<[llvm_ptr_ty], + [llvm_ptr_ty, llvm_ptr_ty], + [IntrNoMem]>; } //===----------------------------------------------------------------------===// Index: llvm/trunk/lib/CodeGen/AsmPrinter/WinException.h =================================================================== --- llvm/trunk/lib/CodeGen/AsmPrinter/WinException.h +++ llvm/trunk/lib/CodeGen/AsmPrinter/WinException.h @@ -50,6 +50,11 @@ void extendIP2StateTable(const MachineFunction *MF, const Function *ParentF, WinEHFuncInfo &FuncInfo); + /// Emits the label used with llvm.x86.seh.recoverfp, which is used by + /// outlined funclets. + void emitEHRegistrationOffsetLabel(const WinEHFuncInfo &FuncInfo, + StringRef FLinkageName); + const MCExpr *create32bitRef(const MCSymbol *Value); const MCExpr *create32bitRef(const GlobalValue *GV); Index: llvm/trunk/lib/CodeGen/AsmPrinter/WinException.cpp =================================================================== --- llvm/trunk/lib/CodeGen/AsmPrinter/WinException.cpp +++ llvm/trunk/lib/CodeGen/AsmPrinter/WinException.cpp @@ -319,6 +319,7 @@ return; } else { FuncInfoXData = Asm->OutContext.getOrCreateLSDASymbol(ParentLinkageName); + emitEHRegistrationOffsetLabel(FuncInfo, ParentLinkageName); } MCSymbol *UnwindMapXData = nullptr; @@ -547,28 +548,33 @@ } } +void WinException::emitEHRegistrationOffsetLabel(const WinEHFuncInfo &FuncInfo, + StringRef FLinkageName) { + // Outlined helpers called by the EH runtime need to know the offset of the EH + // registration in order to recover the parent frame pointer. Now that we know + // we've code generated the parent, we can emit the label assignment that + // those helpers use to get the offset of the registration node. + assert(FuncInfo.EHRegNodeEscapeIndex != INT_MAX && + "no EH reg node frameescape index"); + MCSymbol *ParentFrameOffset = + Asm->OutContext.getOrCreateParentFrameOffsetSymbol(FLinkageName); + MCSymbol *RegistrationOffsetSym = Asm->OutContext.getOrCreateFrameAllocSymbol( + FLinkageName, FuncInfo.EHRegNodeEscapeIndex); + const MCExpr *RegistrationOffsetSymRef = + MCSymbolRefExpr::create(RegistrationOffsetSym, Asm->OutContext); + Asm->OutStreamer->EmitAssignment(ParentFrameOffset, RegistrationOffsetSymRef); +} + /// Emit the language-specific data that _except_handler3 and 4 expect. This is /// functionally equivalent to the __C_specific_handler table, except it is /// indexed by state number instead of IP. void WinException::emitExceptHandlerTable(const MachineFunction *MF) { MCStreamer &OS = *Asm->OutStreamer; - - // Define the EH registration node offset label in terms of its frameescape - // label. The WinEHStatePass ensures that the registration node is passed to - // frameescape. This allows SEH filter functions to access the - // EXCEPTION_POINTERS field, which is filled in by the _except_handlerN. const Function *F = MF->getFunction(); - WinEHFuncInfo &FuncInfo = MMI->getWinEHFuncInfo(F); - assert(FuncInfo.EHRegNodeEscapeIndex != INT_MAX && - "no EH reg node frameescape index"); StringRef FLinkageName = GlobalValue::getRealLinkageName(F->getName()); - MCSymbol *ParentFrameOffset = - Asm->OutContext.getOrCreateParentFrameOffsetSymbol(FLinkageName); - MCSymbol *FrameAllocSym = Asm->OutContext.getOrCreateFrameAllocSymbol( - FLinkageName, FuncInfo.EHRegNodeEscapeIndex); - const MCSymbolRefExpr *FrameAllocSymRef = - MCSymbolRefExpr::create(FrameAllocSym, Asm->OutContext); - OS.EmitAssignment(ParentFrameOffset, FrameAllocSymRef); + + WinEHFuncInfo &FuncInfo = MMI->getWinEHFuncInfo(F); + emitEHRegistrationOffsetLabel(FuncInfo, FLinkageName); // Emit the __ehtable label that we use for llvm.x86.seh.lsda. MCSymbol *LSDALabel = Asm->OutContext.getOrCreateLSDASymbol(FLinkageName); Index: llvm/trunk/lib/Target/X86/X86ISelLowering.cpp =================================================================== --- llvm/trunk/lib/Target/X86/X86ISelLowering.cpp +++ llvm/trunk/lib/Target/X86/X86ISelLowering.cpp @@ -14995,6 +14995,48 @@ return DAG.getNode(X86ISD::SELECT, dl, VT, IMask, Op, PreservedSrc); } +/// When the 32-bit MSVC runtime transfers control to us, either to an outlined +/// function or when returning to a parent frame after catching an exception, we +/// recover the parent frame pointer by doing arithmetic on the incoming EBP. +/// Here's the math: +/// RegNodeBase = EntryEBP - RegNodeSize +/// ParentFP = RegNodeBase - RegNodeFrameOffset +/// Subtracting RegNodeSize takes us to the offset of the registration node, and +/// subtracting the offset (negative on x86) takes us back to the parent FP. +static SDValue recoverFramePointer(SelectionDAG &DAG, const Function *Fn, + SDValue EntryEBP) { + MachineFunction &MF = DAG.getMachineFunction(); + SDLoc dl; + + const TargetLowering &TLI = DAG.getTargetLoweringInfo(); + MVT PtrVT = TLI.getPointerTy(); + + // The RegNodeSize is 6 32-bit words for SEH and 4 for C++ EH. See + // WinEHStatePass for the full struct definition. + int RegNodeSize; + switch (classifyEHPersonality(Fn->getPersonalityFn())) { + default: + report_fatal_error("can only recover FP for MSVC EH personality functions"); + case EHPersonality::MSVC_X86SEH: RegNodeSize = 24; break; + case EHPersonality::MSVC_CXX: RegNodeSize = 16; break; + } + + // Get an MCSymbol that will ultimately resolve to the frame offset of the EH + // registration. + MCSymbol *OffsetSym = + MF.getMMI().getContext().getOrCreateParentFrameOffsetSymbol( + GlobalValue::getRealLinkageName(Fn->getName())); + SDValue OffsetSymVal = DAG.getMCSymbol(OffsetSym, PtrVT); + SDValue RegNodeFrameOffset = + DAG.getNode(ISD::FRAME_ALLOC_RECOVER, dl, PtrVT, OffsetSymVal); + + // RegNodeBase = EntryEBP - RegNodeSize + // ParentFP = RegNodeBase - RegNodeFrameOffset + SDValue RegNodeBase = DAG.getNode(ISD::SUB, dl, PtrVT, EntryEBP, + DAG.getConstant(RegNodeSize, dl, PtrVT)); + return DAG.getNode(ISD::SUB, dl, PtrVT, RegNodeBase, RegNodeFrameOffset); +} + static SDValue LowerINTRINSIC_WO_CHAIN(SDValue Op, const X86Subtarget *Subtarget, SelectionDAG &DAG) { SDLoc dl(Op); @@ -15440,6 +15482,17 @@ SDValue Result = DAG.getMCSymbol(LSDASym, VT); return DAG.getNode(X86ISD::Wrapper, dl, VT, Result); } + + case Intrinsic::x86_seh_recoverfp: { + SDValue FnOp = Op.getOperand(1); + SDValue IncomingFPOp = Op.getOperand(2); + GlobalAddressSDNode *GSD = dyn_cast(FnOp); + auto *Fn = dyn_cast_or_null(GSD ? GSD->getGlobal() : nullptr); + if (!Fn) + report_fatal_error( + "llvm.x86.seh.recoverfp must take a function as the first argument"); + return recoverFramePointer(DAG, Fn, IncomingFPOp); + } } } @@ -15650,35 +15703,38 @@ return DAG.getMergeValues(Results, DL); } -static SDValue LowerEXCEPTIONINFO(SDValue Op, const X86Subtarget *Subtarget, - SelectionDAG &DAG) { +static SDValue LowerSEHRESTOREFRAME(SDValue Op, const X86Subtarget *Subtarget, + SelectionDAG &DAG) { MachineFunction &MF = DAG.getMachineFunction(); SDLoc dl(Op); - SDValue FnOp = Op.getOperand(2); - SDValue FPOp = Op.getOperand(3); + SDValue Chain = Op.getOperand(0); - // Compute the symbol for the parent EH registration. We know it'll get - // emitted later. - auto *Fn = cast(cast(FnOp)->getGlobal()); - MCSymbol *ParentFrameSym = - MF.getMMI().getContext().getOrCreateParentFrameOffsetSymbol( - GlobalValue::getRealLinkageName(Fn->getName())); + const TargetLowering &TLI = DAG.getTargetLoweringInfo(); + MVT VT = TLI.getPointerTy(); - // Create a TargetExternalSymbol for the label to avoid any target lowering - // that would make this PC relative. - MVT PtrVT = Op.getSimpleValueType(); - SDValue OffsetSym = DAG.getMCSymbol(ParentFrameSym, PtrVT); - SDValue OffsetVal = - DAG.getNode(ISD::FRAME_ALLOC_RECOVER, dl, PtrVT, OffsetSym); - - // Add the offset to the FP. - SDValue Add = DAG.getNode(ISD::ADD, dl, PtrVT, FPOp, OffsetVal); - - // Load the second field of the struct, which is 4 bytes in. See - // WinEHStatePass for more info. - Add = DAG.getNode(ISD::ADD, dl, PtrVT, Add, DAG.getConstant(4, dl, PtrVT)); - return DAG.getLoad(PtrVT, dl, DAG.getEntryNode(), Add, MachinePointerInfo(), - false, false, false, 0); + const X86RegisterInfo *RegInfo = Subtarget->getRegisterInfo(); + unsigned FrameReg = + RegInfo->getPtrSizedFrameRegister(DAG.getMachineFunction()); + unsigned SPReg = RegInfo->getStackRegister(); + + // Get incoming EBP. + SDValue IncomingEBP = + DAG.getCopyFromReg(Chain, dl, FrameReg, VT); + + // Load [EBP-24] into SP. + SDValue SPAddr = + DAG.getNode(ISD::ADD, dl, VT, IncomingEBP, DAG.getConstant(-24, dl, VT)); + SDValue NewSP = + DAG.getLoad(VT, dl, Chain, SPAddr, MachinePointerInfo(), false, false, + false, VT.getScalarSizeInBits() / 8); + Chain = DAG.getCopyToReg(Chain, dl, SPReg, NewSP); + + // FIXME: Restore the base pointer in case of stack realignment! + + // Adjust EBP to point back to the original frame position. + SDValue NewFP = recoverFramePointer(DAG, MF.getFunction(), IncomingEBP); + Chain = DAG.getCopyToReg(Chain, dl, FrameReg, NewFP); + return Chain; } static SDValue LowerINTRINSIC_W_CHAIN(SDValue Op, const X86Subtarget *Subtarget, @@ -15687,8 +15743,8 @@ const IntrinsicData* IntrData = getIntrinsicWithChain(IntNo); if (!IntrData) { - if (IntNo == Intrinsic::x86_seh_exceptioninfo) - return LowerEXCEPTIONINFO(Op, Subtarget, DAG); + if (IntNo == llvm::Intrinsic::x86_seh_restoreframe) + return LowerSEHRESTOREFRAME(Op, Subtarget, DAG); return SDValue(); } Index: llvm/trunk/lib/Target/X86/X86WinEHState.cpp =================================================================== --- llvm/trunk/lib/Target/X86/X86WinEHState.cpp +++ llvm/trunk/lib/Target/X86/X86WinEHState.cpp @@ -398,6 +398,7 @@ // Set up RegNodeEscapeIndex int RegNodeEscapeIndex = escapeRegNode(F); + FuncInfo.EHRegNodeEscapeIndex = RegNodeEscapeIndex; // Only insert stores in catch handlers. Constant *FI8 = @@ -480,8 +481,8 @@ WinEHFuncInfo &FuncInfo = MMI.getWinEHFuncInfo(&F); // Remember and return the index that we used. We save it in WinEHFuncInfo so - // that we can lower llvm.x86.seh.exceptioninfo later in filter functions - // without too much trouble. + // that we can lower llvm.x86.seh.recoverfp later in filter functions without + // too much trouble. int RegNodeEscapeIndex = escapeRegNode(F); FuncInfo.EHRegNodeEscapeIndex = RegNodeEscapeIndex; @@ -528,14 +529,12 @@ } } - // Insert llvm.stackrestore into each __except block. - Function *StackRestore = - Intrinsic::getDeclaration(TheModule, Intrinsic::stackrestore); + // Insert llvm.x86.seh.restoreframe() into each __except block. + Function *RestoreFrame = + Intrinsic::getDeclaration(TheModule, Intrinsic::x86_seh_restoreframe); for (BasicBlock *ExceptBB : ExceptBlocks) { IRBuilder<> Builder(ExceptBB->begin()); - Value *SP = - Builder.CreateLoad(Builder.CreateStructGEP(RegNodeTy, RegNode, 0)); - Builder.CreateCall(StackRestore, {SP}); + Builder.CreateCall(RestoreFrame, {}); } } Index: llvm/trunk/test/CodeGen/X86/seh-catch-all-win32.ll =================================================================== --- llvm/trunk/test/CodeGen/X86/seh-catch-all-win32.ll +++ llvm/trunk/test/CodeGen/X86/seh-catch-all-win32.ll @@ -12,7 +12,7 @@ declare i8* @llvm.frameaddress(i32) declare i8* @llvm.framerecover(i8*, i8*, i32) declare void @llvm.frameescape(...) -declare i8* @llvm.x86.seh.exceptioninfo(i8*, i8*) +declare i8* @llvm.x86.seh.recoverfp(i8*, i8*) define i32 @main() personality i8* bitcast (i32 (...)* @_except_handler3 to i8*) { entry: @@ -43,14 +43,16 @@ define internal i32 @"filt$main"() { entry: - %0 = tail call i8* @llvm.frameaddress(i32 1) - %1 = tail call i8* @llvm.framerecover(i8* bitcast (i32 ()* @main to i8*), i8* %0, i32 0) - %__exceptioncode = bitcast i8* %1 to i32* - %2 = tail call i8* @llvm.x86.seh.exceptioninfo(i8* bitcast (i32 ()* @main to i8*), i8* %0) - %3 = bitcast i8* %2 to i32** - %4 = load i32*, i32** %3, align 4 - %5 = load i32, i32* %4, align 4 - store i32 %5, i32* %__exceptioncode, align 4 + %ebp = tail call i8* @llvm.frameaddress(i32 1) + %parentfp = tail call i8* @llvm.x86.seh.recoverfp(i8* bitcast (i32 ()* @main to i8*), i8* %ebp) + %code.i8 = tail call i8* @llvm.framerecover(i8* bitcast (i32 ()* @main to i8*), i8* %parentfp, i32 0) + %__exceptioncode = bitcast i8* %code.i8 to i32* + %info.addr = getelementptr inbounds i8, i8* %ebp, i32 -20 + %0 = bitcast i8* %info.addr to i32*** + %1 = load i32**, i32*** %0, align 4 + %2 = load i32*, i32** %1, align 4 + %3 = load i32, i32* %2, align 4 + store i32 %3, i32* %__exceptioncode, align 4 ret i32 1 } @@ -76,10 +78,17 @@ ; CHECK: calll _printf ; CHECK: .section .xdata,"dr" +; CHECK: Lmain$parent_frame_offset = Lmain$frame_escape_1 ; CHECK: L__ehtable$main ; CHECK-NEXT: .long -1 ; CHECK-NEXT: .long _filt$main ; CHECK-NEXT: .long Ltmp{{[0-9]+}} ; CHECK-LABEL: _filt$main: -; CHECK: movl +; CHECK: pushl %ebp +; CHECK: movl %esp, %ebp +; CHECK: movl (%ebp), %[[oldebp:[a-z]+]] +; CHECK: movl -20(%[[oldebp]]), %[[ehinfo:[a-z]+]] +; CHECK: movl (%[[ehinfo]]), %[[ehrec:[a-z]+]] +; CHECK: movl (%[[ehrec]]), %[[ehcode:[a-z]+]] +; CHECK: movl %[[ehcode]], {{.*}}(%{{.*}}) Index: llvm/trunk/test/CodeGen/X86/seh-safe-div-win32.ll =================================================================== --- llvm/trunk/test/CodeGen/X86/seh-safe-div-win32.ll +++ llvm/trunk/test/CodeGen/X86/seh-safe-div-win32.ll @@ -122,27 +122,30 @@ ; ... ; } EXCEPTION_RECORD; -; FIXME: Use llvm.eh.exceptioninfo for this. -declare i32 @safe_div_filt0() -declare i32 @safe_div_filt1() -; define i32 @safe_div_filt0() { -; %eh_ptrs_c = bitcast i8* %eh_ptrs to i32** -; %eh_rec = load i32*, i32** %eh_ptrs_c -; %eh_code = load i32, 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() { -; %eh_ptrs_c = bitcast i8* %eh_ptrs to i32** -; %eh_rec = load i32*, i32** %eh_ptrs_c -; %eh_code = load i32, 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 -; } +define i32 @safe_div_filt0() { + %ebp = call i8* @llvm.frameaddress(i32 1) + %eh_ptrs.addr.i8 = getelementptr inbounds i8, i8* %ebp, i32 -20 + %eh_ptrs.addr = bitcast i8* %eh_ptrs.addr.i8 to i32*** + %eh_ptrs = load i32**, i32*** %eh_ptrs.addr + %eh_rec = load i32*, i32** %eh_ptrs + %eh_code = load i32, 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() { + %ebp = call i8* @llvm.frameaddress(i32 1) + %eh_ptrs.addr.i8 = getelementptr inbounds i8, i8* %ebp, i32 -20 + %eh_ptrs.addr = bitcast i8* %eh_ptrs.addr.i8 to i32*** + %eh_ptrs = load i32**, i32*** %eh_ptrs.addr + %eh_rec = load i32*, i32** %eh_ptrs + %eh_code = load i32, 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" @@ -170,3 +173,4 @@ declare void @puts(i8*) declare void @printf(i8*, ...) declare void @abort() +declare i8* @llvm.frameaddress(i32)