From a894d59f4a5a130834b312ed97ad9a92b1e40e6a Mon Sep 17 00:00:00 2001 From: Reid Kleckner Date: Wed, 25 Mar 2015 20:10:36 +0000 Subject: [PATCH] WinEH: Create an unwind help alloca for __CxxFrameHandler3 xdata tables We don't have any logic to emit those tables yet, so the sdag lowering of this intrinsic is just a stub. We can see the intrinsic in the prepared IR, though. llvm-svn: 233209 --- docs/ExceptionHandling.rst | 12 ++++++++++++ include/llvm/IR/Intrinsics.td | 4 ++++ lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp | 10 ++++++++++ lib/CodeGen/WinEHPrepare.cpp | 13 +++++++++++++ lib/IR/Verifier.cpp | 7 +++++++ test/CodeGen/WinEH/cppeh-catch-unwind.ll | 3 +++ 6 files changed, 49 insertions(+) diff --git a/docs/ExceptionHandling.rst b/docs/ExceptionHandling.rst index ee157d096a2..21de19b0752 100644 --- a/docs/ExceptionHandling.rst +++ b/docs/ExceptionHandling.rst @@ -539,6 +539,18 @@ In order to preserve the structure of the CFG, a call to '``llvm.eh.actions``' must be followed by an ':ref:`indirectbr `' instruction that jumps to the result of the intrinsic call. +``llvm.eh.unwindhelp`` +---------------------- + +.. code-block:: llvm + + void @llvm.eh.unwindhelp(i8*) + +This intrinsic designates the provided static alloca as the unwind help object. +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. + SJLJ Intrinsics --------------- diff --git a/include/llvm/IR/Intrinsics.td b/include/llvm/IR/Intrinsics.td index 110d6b22e8a..da9d8cb61f5 100644 --- a/include/llvm/IR/Intrinsics.td +++ b/include/llvm/IR/Intrinsics.td @@ -421,6 +421,10 @@ def int_eh_endcatch : Intrinsic<[], []>; // Represents the list of actions to take when an exception is thrown. def int_eh_actions : Intrinsic<[llvm_ptr_ty], [llvm_vararg_ty], []>; +// Designates the provided static alloca as the unwind help object. Required +// for WinEH. +def int_eh_unwindhelp : Intrinsic<[], [llvm_ptr_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/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp index 6f5ea7c7469..6c14e7969c5 100644 --- a/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ b/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -5446,6 +5446,16 @@ 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_unwindhelp: { + AllocaInst *Slot = + cast(I.getArgOperand(0)->stripPointerCasts()); + assert(FuncInfo.StaticAllocaMap.count(Slot) && + "can only use static allocas with llvm.eh.unwindhelp"); + int FI = FuncInfo.StaticAllocaMap[Slot]; + // TODO: Save this in the not-yet-existant WinEHFuncInfo struct. + (void)FI; + return nullptr; + } } } diff --git a/lib/CodeGen/WinEHPrepare.cpp b/lib/CodeGen/WinEHPrepare.cpp index d1b385579b9..ab0f96ef05f 100644 --- a/lib/CodeGen/WinEHPrepare.cpp +++ b/lib/CodeGen/WinEHPrepare.cpp @@ -601,6 +601,19 @@ bool WinEHPrepare::prepareExceptionHandlers( Builder.SetInsertPoint(&F.getEntryBlock().back()); Builder.CreateCall(FrameEscapeFn, AllocasToEscape); + // Insert an alloca for the EH state in the entry block. On x86, we will also + // insert stores to update the EH state, but on other ISAs, the runtime does + // it for us. + // FIXME: This record is different on x86. + Type *UnwindHelpTy = Type::getInt64Ty(Context); + AllocaInst *UnwindHelp = + new AllocaInst(UnwindHelpTy, "unwindhelp", &F.getEntryBlock().front()); + Builder.CreateStore(llvm::ConstantInt::get(UnwindHelpTy, -2), UnwindHelp); + Function *UnwindHelpFn = + Intrinsic::getDeclaration(M, Intrinsic::eh_unwindhelp); + Builder.CreateCall(UnwindHelpFn, + Builder.CreateBitCast(UnwindHelp, Int8PtrType)); + // Clean up the handler action maps we created for this function DeleteContainerSeconds(CatchHandlerMap); CatchHandlerMap.clear(); diff --git a/lib/IR/Verifier.cpp b/lib/IR/Verifier.cpp index 5f25891f2b1..ee3582d00ca 100644 --- a/lib/IR/Verifier.cpp +++ b/lib/IR/Verifier.cpp @@ -2908,6 +2908,13 @@ void Verifier::visitIntrinsicFunctionCall(Intrinsic::ID ID, CallInst &CI) { break; } + case Intrinsic::eh_unwindhelp: { + auto *AI = dyn_cast(CI.getArgOperand(0)->stripPointerCasts()); + Assert(AI && AI->isStaticAlloca(), + "llvm.eh.unwindhelp requires a static alloca", &CI); + break; + } + case Intrinsic::experimental_gc_statepoint: Assert(!CI.isInlineAsm(), "gc.statepoint support for inline assembly unimplemented", &CI); diff --git a/test/CodeGen/WinEH/cppeh-catch-unwind.ll b/test/CodeGen/WinEH/cppeh-catch-unwind.ll index 011f2df96ed..3db1635a110 100644 --- a/test/CodeGen/WinEH/cppeh-catch-unwind.ll +++ b/test/CodeGen/WinEH/cppeh-catch-unwind.ll @@ -33,10 +33,13 @@ $"\01??_R0H@8" = comdat any ; CHECK-LABEL: define void @"\01?test@@YAXXZ"() #0 { ; CHECK: entry: +; CHECK: [[UNWIND_HELP:\%.+]] = alloca i64 ; CHECK: [[OBJ_PTR:\%.+]] = alloca %class.SomeClass ; CHECK: [[TMP0:\%.+]] = alloca i32, align 4 ; CHECK: [[TMP1:\%.+]] = alloca i32, align 4 ; CHECK: call void (...)* @llvm.frameescape(i32* [[TMP1]], %class.SomeClass* [[OBJ_PTR]], i32* [[TMP0]]) +; CHECK: [[UNWIND_HELP_i8:\%.+]] = bitcast i64* [[UNWIND_HELP]] to i8* +; CHECK: call void @llvm.eh.unwindhelp(i8* [[UNWIND_HELP_i8]]) ; CHECK: %call = invoke %class.SomeClass* @"\01??0SomeClass@@QEAA@XZ"(%class.SomeClass* %obj) ; CHECK: to label %invoke.cont unwind label %[[LPAD_LABEL:lpad[0-9]+]]