From 3e16bd3aaf5055ea94d9068210e98ae5ed4da78b Mon Sep 17 00:00:00 2001 From: Reid Kleckner Date: Thu, 11 Jun 2015 22:32:23 +0000 Subject: [PATCH] [WinEH] Create an llvm.x86.seh.exceptioninfo intrinsic This intrinsic is like framerecover plus a load. It recovers the EH registration stack allocation from the parent frame and loads the exception information field out of it, giving back a pointer to an EXCEPTION_POINTERS struct. It's designed for clang to use in SEH filter expressions instead of accessing the EXCEPTION_POINTERS parameter that is available on x64. This required a minor change to MC to allow defining a label variable to another absolute framerecover label variable. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@239567 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/llvm/CodeGen/WinEHFuncInfo.h | 13 ++-- include/llvm/IR/Intrinsics.td | 1 - include/llvm/IR/IntrinsicsX86.td | 5 +- lib/CodeGen/AsmPrinter/WinException.cpp | 21 +++++- lib/MC/WinCOFFObjectWriter.cpp | 9 ++- lib/Target/X86/X86ISelLowering.cpp | 54 ++++++++++------ lib/Target/X86/X86WinEHState.cpp | 35 ++-------- test/CodeGen/X86/seh-catch-all-win32.ll | 85 +++++++++++++++++++++++++ test/CodeGen/X86/seh-catch-all.ll | 56 +++++----------- test/CodeGen/X86/seh-safe-div-win32.ll | 2 +- 10 files changed, 176 insertions(+), 105 deletions(-) create mode 100644 test/CodeGen/X86/seh-catch-all-win32.ll diff --git a/include/llvm/CodeGen/WinEHFuncInfo.h b/include/llvm/CodeGen/WinEHFuncInfo.h index 1cff3203f2b..291f3905512 100644 --- a/include/llvm/CodeGen/WinEHFuncInfo.h +++ b/include/llvm/CodeGen/WinEHFuncInfo.h @@ -144,14 +144,15 @@ struct WinEHFuncInfo { SmallVector UnwindMap; SmallVector TryBlockMap; SmallVector, 4> IPToStateList; - int UnwindHelpFrameIdx; - int UnwindHelpFrameOffset; + int UnwindHelpFrameIdx = INT_MAX; + int UnwindHelpFrameOffset = -1; + unsigned NumIPToStateFuncsVisited = 0; - unsigned NumIPToStateFuncsVisited; + /// frameescape index of the 32-bit EH registration node. Set by + /// WinEHStatePass and used indirectly by SEH filter functions of the parent. + int EHRegNodeEscapeIndex = INT_MAX; - WinEHFuncInfo() - : UnwindHelpFrameIdx(INT_MAX), UnwindHelpFrameOffset(-1), - NumIPToStateFuncsVisited(0) {} + WinEHFuncInfo() {} }; /// Analyze the IR in ParentFn and it's handlers to build WinEHFuncInfo, which diff --git a/include/llvm/IR/Intrinsics.td b/include/llvm/IR/Intrinsics.td index c4c57410395..152c7fcfcca 100644 --- a/include/llvm/IR/Intrinsics.td +++ b/include/llvm/IR/Intrinsics.td @@ -429,7 +429,6 @@ def int_eh_endcatch : Intrinsic<[], []>; def int_eh_actions : Intrinsic<[llvm_ptr_ty], [llvm_vararg_ty], []>; def int_eh_exceptioncode : Intrinsic<[llvm_i32_ty], [], [IntrReadMem]>; -def int_eh_exceptioninfo : Intrinsic<[llvm_ptr_ty], [], [IntrReadMem]>; // __builtin_unwind_init is an undocumented GCC intrinsic that causes all // callee-saved registers to be saved and restored (regardless of whether they diff --git a/include/llvm/IR/IntrinsicsX86.td b/include/llvm/IR/IntrinsicsX86.td index 0826aa2287e..cb0f53d71b8 100644 --- a/include/llvm/IR/IntrinsicsX86.td +++ b/include/llvm/IR/IntrinsicsX86.td @@ -18,9 +18,12 @@ let TargetPrefix = "x86" in { // All intrinsics start with "llvm.x86.". } //===----------------------------------------------------------------------===// -// SEH LSDA for Windows +// 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]>; } //===----------------------------------------------------------------------===// diff --git a/lib/CodeGen/AsmPrinter/WinException.cpp b/lib/CodeGen/AsmPrinter/WinException.cpp index 40d6bab8b6d..75287fd9855 100644 --- a/lib/CodeGen/AsmPrinter/WinException.cpp +++ b/lib/CodeGen/AsmPrinter/WinException.cpp @@ -449,7 +449,7 @@ void WinException::emitCXXFrameHandler3Table(const MachineFunction *MF) { Asm->OutContext.getOrCreateParentFrameOffsetSymbol( GlobalValue::getRealLinkageName(HT.Handler->getName())); const MCSymbolRefExpr *ParentFrameOffsetRef = MCSymbolRefExpr::create( - ParentFrameOffset, MCSymbolRefExpr::VK_None, Asm->OutContext); + ParentFrameOffset, Asm->OutContext); OS.EmitValue(ParentFrameOffsetRef, 4); // ParentFrameOffset } } @@ -551,11 +551,26 @@ void WinException::extendIP2StateTable(const MachineFunction *MF, /// functionally equivalent to the __C_specific_handler table, except it is /// indexed by state number instead of IP. void WinException::emitExceptHandlerTable(const MachineFunction *MF) { - auto &OS = *Asm->OutStreamer; + MCStreamer &OS = *Asm->OutStreamer; - // Emit the __ehtable label that we use for llvm.x86.seh.lsda. + // 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); + + // Emit the __ehtable label that we use for llvm.x86.seh.lsda. MCSymbol *LSDALabel = Asm->OutContext.getOrCreateLSDASymbol(FLinkageName); OS.EmitLabel(LSDALabel); diff --git a/lib/MC/WinCOFFObjectWriter.cpp b/lib/MC/WinCOFFObjectWriter.cpp index 423c7dce45d..56ef1c7a273 100644 --- a/lib/MC/WinCOFFObjectWriter.cpp +++ b/lib/MC/WinCOFFObjectWriter.cpp @@ -526,13 +526,12 @@ bool WinCOFFObjectWriter::ExportSymbol(const MCSymbol &Symbol, if (!Symbol.isTemporary()) return true; - // Absolute temporary labels are never visible. - if (!Symbol.isInSection()) + // Temporary variable symbols are invisible. + if (Symbol.isVariable()) return false; - // For now, all non-variable symbols are exported, - // the linker will sort the rest out for us. - return !Symbol.isVariable(); + // Absolute temporary labels are never visible. + return !Symbol.isAbsolute(); } bool WinCOFFObjectWriter::IsPhysicalSection(COFFSection *S) { diff --git a/lib/Target/X86/X86ISelLowering.cpp b/lib/Target/X86/X86ISelLowering.cpp index 8e663097d71..a4c7d1620fc 100644 --- a/lib/Target/X86/X86ISelLowering.cpp +++ b/lib/Target/X86/X86ISelLowering.cpp @@ -15506,23 +15506,6 @@ static SDValue LowerINTRINSIC_WO_CHAIN(SDValue Op, const X86Subtarget *Subtarget DAG.getTargetExternalSymbol(Name.data(), VT, X86II::MO_NOPREFIX); return DAG.getNode(X86ISD::Wrapper, dl, VT, Result); } - - case Intrinsic::eh_exceptioninfo: { - // Compute the symbol for the LSDA. We know it'll get emitted later. - MachineFunction &MF = DAG.getMachineFunction(); - SDValue Op1 = Op.getOperand(1); - auto *Fn = cast(cast(Op1)->getGlobal()); - MCSymbol *LSDASym = MF.getMMI().getContext().getOrCreateLSDASymbol( - GlobalValue::getRealLinkageName(Fn->getName())); - StringRef Name = LSDASym->getName(); - assert(Name.data()[Name.size()] == '\0' && "not null terminated"); - - // Generate a simple absolute symbol reference. This intrinsic is only - // supported on 32-bit Windows, which isn't PIC. - SDValue Result = - DAG.getTargetExternalSymbol(Name.data(), VT, X86II::MO_NOPREFIX); - return DAG.getNode(X86ISD::Wrapper, dl, VT, Result); - } } } @@ -15707,14 +15690,49 @@ static SDValue LowerREADCYCLECOUNTER(SDValue Op, const X86Subtarget *Subtarget, return DAG.getMergeValues(Results, DL); } +static SDValue LowerEXCEPTIONINFO(SDValue Op, const X86Subtarget *Subtarget, + SelectionDAG &DAG) { + MachineFunction &MF = DAG.getMachineFunction(); + SDLoc dl(Op); + SDValue FnOp = Op.getOperand(2); + SDValue FPOp = Op.getOperand(3); + + // 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())); + StringRef Name = ParentFrameSym->getName(); + assert(Name.data()[Name.size()] == '\0' && "not null terminated"); + + // Create a TargetExternalSymbol for the label to avoid any target lowering + // that would make this PC relative. + MVT PtrVT = Op.getSimpleValueType(); + SDValue OffsetSym = DAG.getTargetExternalSymbol(Name.data(), 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); +} static SDValue LowerINTRINSIC_W_CHAIN(SDValue Op, const X86Subtarget *Subtarget, SelectionDAG &DAG) { unsigned IntNo = cast(Op.getOperand(1))->getZExtValue(); const IntrinsicData* IntrData = getIntrinsicWithChain(IntNo); - if (!IntrData) + if (!IntrData) { + if (IntNo == Intrinsic::x86_seh_exceptioninfo) + return LowerEXCEPTIONINFO(Op, Subtarget, DAG); return SDValue(); + } SDLoc dl(Op); switch(IntrData->Type) { diff --git a/lib/Target/X86/X86WinEHState.cpp b/lib/Target/X86/X86WinEHState.cpp index afad3f930da..e037f08790c 100644 --- a/lib/Target/X86/X86WinEHState.cpp +++ b/lib/Target/X86/X86WinEHState.cpp @@ -67,8 +67,6 @@ private: void addCXXStateStoresToFunclet(Value *ParentRegNode, WinEHFuncInfo &FuncInfo, Function &F, int BaseState); void insertStateNumberStore(Value *ParentRegNode, Instruction *IP, int State); - iplist::iterator - rewriteExceptionInfoIntrinsics(IntrinsicInst *Intrin); Value *emitEHLSDA(IRBuilder<> &Builder, Function *F); @@ -487,6 +485,12 @@ void WinEHStatePass::addCXXStateStoresToFunclet(Value *ParentRegNode, void WinEHStatePass::addSEHStateStores(Function &F, MachineModuleInfo &MMI) { 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. + int RegNodeEscapeIndex = escapeRegNode(F); + FuncInfo.EHRegNodeEscapeIndex = RegNodeEscapeIndex; + // Iterate all the instructions and emit state number stores. int CurState = 0; SmallPtrSet ExceptBlocks; @@ -495,7 +499,6 @@ void WinEHStatePass::addSEHStateStores(Function &F, MachineModuleInfo &MMI) { if (auto *CI = dyn_cast(I)) { auto *Intrin = dyn_cast(CI); if (Intrin) { - I = rewriteExceptionInfoIntrinsics(Intrin); // Calls that "don't throw" are considered to be able to throw asynch // exceptions, but intrinsics cannot. continue; @@ -542,32 +545,6 @@ void WinEHStatePass::addSEHStateStores(Function &F, MachineModuleInfo &MMI) { } } -/// Rewrite llvm.eh.exceptioncode and llvm.eh.exceptioninfo to memory loads in -/// IR. -iplist::iterator -WinEHStatePass::rewriteExceptionInfoIntrinsics(IntrinsicInst *Intrin) { - Intrinsic::ID ID = Intrin->getIntrinsicID(); - if (ID != Intrinsic::eh_exceptioncode && ID != Intrinsic::eh_exceptioninfo) - return Intrin; - - // RegNode->ExceptionPointers - IRBuilder<> Builder(Intrin); - Value *Ptrs = - Builder.CreateLoad(Builder.CreateStructGEP(RegNodeTy, RegNode, 1)); - Value *Res; - if (ID == Intrinsic::eh_exceptioncode) { - // Ptrs->ExceptionRecord->Code - Ptrs = Builder.CreateBitCast( - Ptrs, Builder.getInt32Ty()->getPointerTo()->getPointerTo()); - Value *Rec = Builder.CreateLoad(Ptrs); - Res = Builder.CreateLoad(Rec); - } else { - Res = Ptrs; - } - Intrin->replaceAllUsesWith(Res); - return Intrin->eraseFromParent(); -} - void WinEHStatePass::insertStateNumberStore(Value *ParentRegNode, Instruction *IP, int State) { IRBuilder<> Builder(IP); diff --git a/test/CodeGen/X86/seh-catch-all-win32.ll b/test/CodeGen/X86/seh-catch-all-win32.ll new file mode 100644 index 00000000000..0c6b7f77ac8 --- /dev/null +++ b/test/CodeGen/X86/seh-catch-all-win32.ll @@ -0,0 +1,85 @@ +; RUN: llc -mtriple=i686-windows-msvc < %s | FileCheck %s + +; 32-bit catch-all has to use a filter function because that's how it saves the +; exception code. + +@str = linkonce_odr unnamed_addr constant [27 x i8] c"GetExceptionCode(): 0x%lx\0A\00", align 1 + +declare i32 @_except_handler3(...) +declare void @crash() +declare i32 @printf(i8* nocapture readonly, ...) nounwind +declare i32 @llvm.eh.typeid.for(i8*) +declare i8* @llvm.frameaddress(i32) +declare i8* @llvm.framerecover(i8*, i8*, i32) +declare void @llvm.frameescape(...) +declare i8* @llvm.x86.seh.exceptioninfo(i8*, i8*) + +define i32 @main() { +entry: + %__exceptioncode = alloca i32, align 4 + call void (...) @llvm.frameescape(i32* %__exceptioncode) + invoke void @crash() #5 + to label %__try.cont unwind label %lpad + +lpad: ; preds = %entry + %0 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @_except_handler3 to i8*) + catch i8* bitcast (i32 ()* @"filt$main" to i8*) + %1 = extractvalue { i8*, i32 } %0, 1 + %2 = call i32 @llvm.eh.typeid.for(i8* bitcast (i32 ()* @"filt$main" to i8*)) #4 + %matches = icmp eq i32 %1, %2 + br i1 %matches, label %__except, label %eh.resume + +__except: ; preds = %lpad + %3 = load i32, i32* %__exceptioncode, align 4 + %call = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([27 x i8], [27 x i8]* @str, i32 0, i32 0), i32 %3) #4 + br label %__try.cont + +__try.cont: ; preds = %entry, %__except + ret i32 0 + +eh.resume: ; preds = %lpad + resume { i8*, i32 } %0 +} + +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 + ret i32 1 +} + +; Check that we can get the exception code from eax to the printf. + +; CHECK-LABEL: _main: +; CHECK: Lmain$frame_escape_0 = [[code_offs:[-0-9]+]] +; CHECK: Lmain$frame_escape_1 = [[reg_offs:[-0-9]+]] +; CHECK: movl %esp, [[reg_offs]](%ebp) +; CHECK: movl $L__ehtable$main, +; EH state 0 +; CHECK: movl $0, -4(%ebp) +; CHECK: calll _crash +; CHECK: retl +; CHECK: # Block address taken +; stackrestore +; CHECK: movl [[reg_offs]](%ebp), %esp +; EH state -1 +; CHECK: movl [[code_offs]](%ebp), %[[code:[a-z]+]] +; CHECK: movl $-1, -4(%ebp) +; CHECK-DAG: movl %[[code]], 4(%esp) +; CHECK-DAG: movl $_str, (%esp) +; CHECK: calll _printf + +; CHECK: .section .xdata,"dr" +; 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 diff --git a/test/CodeGen/X86/seh-catch-all.ll b/test/CodeGen/X86/seh-catch-all.ll index 5586f95dba0..c40e0105353 100644 --- a/test/CodeGen/X86/seh-catch-all.ll +++ b/test/CodeGen/X86/seh-catch-all.ll @@ -1,6 +1,4 @@ -; RUN: llc -mtriple=x86_64-windows-msvc < %s | FileCheck %s --check-prefix=X64 -; RUN: sed -e 's/__C_specific_handler/_except_handler3/' %s | \ -; RUN: llc -mtriple=i686-windows-msvc | FileCheck %s --check-prefix=X86 +; RUN: llc -mtriple=x86_64-windows-msvc < %s | FileCheck %s @str = linkonce_odr unnamed_addr constant [27 x i8] c"GetExceptionCode(): 0x%lx\0A\00", align 1 @@ -31,41 +29,17 @@ eh.resume: ; Check that we can get the exception code from eax to the printf. -; X64-LABEL: main: -; X64: callq crash -; X64: retq -; X64: # Block address taken -; X64: leaq str(%rip), %rcx -; X64: movl %eax, %edx -; X64: callq printf - -; X64: .seh_handlerdata -; X64-NEXT: .long 1 -; X64-NEXT: .long .Ltmp{{[0-9]+}}@IMGREL -; X64-NEXT: .long .Ltmp{{[0-9]+}}@IMGREL+1 -; X64-NEXT: .long 1 -; X64-NEXT: .long .Ltmp{{[0-9]+}}@IMGREL - -; X86-LABEL: _main: -; The EH code load should be this offset +4. -; X86: movl %esp, -24(%ebp) -; X86: movl $L__ehtable$main, -; EH state 0 -; X86: movl $0, -4(%ebp) -; X86: calll _crash -; X86: retl -; X86: # Block address taken -; X86: movl -20(%ebp), %[[ptrs:[^ ,]*]] -; X86: movl (%[[ptrs]]), %[[rec:[^ ,]*]] -; X86: movl (%[[rec]]), %[[code:[^ ,]*]] -; EH state -1 -; X86: movl $-1, -4(%ebp) -; X86-DAG: movl %[[code]], 4(%esp) -; X86-DAG: movl $_str, (%esp) -; X86: calll _printf - -; X86: .section .xdata,"dr" -; X86-NEXT: L__ehtable$main -; X86-NEXT: .long -1 -; X86-NEXT: .long 0 -; X86-NEXT: .long Ltmp{{[0-9]+}} +; CHECK-LABEL: main: +; CHECK: callq crash +; CHECK: retq +; CHECK: # Block address taken +; CHECK: leaq str(%rip), %rcx +; CHECK: movl %eax, %edx +; CHECK: callq printf + +; CHECK: .seh_handlerdata +; CHECK-NEXT: .long 1 +; CHECK-NEXT: .long .Ltmp{{[0-9]+}}@IMGREL +; CHECK-NEXT: .long .Ltmp{{[0-9]+}}@IMGREL+1 +; CHECK-NEXT: .long 1 +; CHECK-NEXT: .long .Ltmp{{[0-9]+}}@IMGREL diff --git a/test/CodeGen/X86/seh-safe-div-win32.ll b/test/CodeGen/X86/seh-safe-div-win32.ll index e5cbc587bb2..ab58a4b99ca 100644 --- a/test/CodeGen/X86/seh-safe-div-win32.ll +++ b/test/CodeGen/X86/seh-safe-div-win32.ll @@ -90,7 +90,7 @@ __try.cont: ; CHECK: jmp [[cont_bb]] ; CHECK: .section .xdata,"dr" -; CHECK-NEXT: L__ehtable$safe_div: +; CHECK: L__ehtable$safe_div: ; CHECK-NEXT: .long -1 ; CHECK-NEXT: .long _safe_div_filt0 ; CHECK-NEXT: .long [[handler0]] -- 2.34.1