diff --git a/llvm/lib/Transforms/Utils/InlineFunction.cpp b/llvm/lib/Transforms/Utils/InlineFunction.cpp --- a/llvm/lib/Transforms/Utils/InlineFunction.cpp +++ b/llvm/lib/Transforms/Utils/InlineFunction.cpp @@ -1833,8 +1833,9 @@ // We need to figure out which funclet the callsite was in so that we may // properly nest the callee. Instruction *CallSiteEHPad = nullptr; + EHPersonality Personality = EHPersonality::Unknown; if (CallerPersonality) { - EHPersonality Personality = classifyEHPersonality(CallerPersonality); + Personality = classifyEHPersonality(CallerPersonality); if (isScopedEHPersonality(Personality)) { Optional ParentFunclet = CB.getOperandBundle(LLVMContext::OB_funclet); @@ -2305,11 +2306,16 @@ if (!I) continue; - // Skip call sites which are nounwind intrinsics. - auto *CalledFn = - dyn_cast(I->getCalledOperand()->stripPointerCasts()); - if (CalledFn && CalledFn->isIntrinsic() && I->doesNotThrow()) - continue; + // In general, skip call sites which are nounwind intrinsics. When + // targeting WinEH, we must propagate funclet tokens to pre-ISel + // intrinsics, if they get inlined into EH funclets. Otherwise, + // WinEHPrepare would mark them unreachable and cause truncations. + if (Personality != EHPersonality::MSVC_CXX) { + auto *CalledFn = + dyn_cast(I->getCalledOperand()->stripPointerCasts()); + if (CalledFn && CalledFn->isIntrinsic() && I->doesNotThrow()) + continue; + } // Skip call sites which already have a "funclet" bundle. if (I->getOperandBundle(LLVMContext::OB_funclet)) diff --git a/llvm/test/Feature/OperandBundles/inliner-funclet-wineh.ll b/llvm/test/Feature/OperandBundles/inliner-funclet-wineh.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Feature/OperandBundles/inliner-funclet-wineh.ll @@ -0,0 +1,55 @@ +; RUN: opt -S -always-inline -mtriple=x86_64-windows-msvc < %s | FileCheck %s + +; WinEH doesn't require funclet tokens on nounwind intrinsics per se. ObjC++ ARC +; intrinsics are a special case, because they are subject to pre-ISel lowering. +; They appear as regular function calls for subsequent passes, like WinEHPrepare +; which would consider them implausible instrucitons and mark them unreachable. +; Affected EH funclets would get truncated silently, which causes unpredictable +; crashes at runtime. +; +; Thus, when we target WinEH and generate calls to pre-ISel intrinsics from EH +; funclets, we emit funclet tokens explicitly. +; +; The inliner has to propagate funclet tokens to such intrinsics, if they get +; inlined into EH funclets. + +define void @inlined_fn(ptr %ex) #1 { +entry: + call void @llvm.objc.storeStrong(ptr %ex, ptr null) + ret void +} + +define void @test_catch_with_inline() personality ptr @__CxxFrameHandler3 { +entry: + %exn.slot = alloca ptr, align 8 + %ex = alloca ptr, align 8 + invoke void @opaque() to label %invoke.cont unwind label %catch.dispatch + +catch.dispatch: + %0 = catchswitch within none [label %catch] unwind to caller + +invoke.cont: + unreachable + +catch: + %1 = catchpad within %0 [ptr null, i32 64, ptr %exn.slot] + call void @inlined_fn(ptr %ex) [ "funclet"(token %1) ] + catchret from %1 to label %catchret.dest + +catchret.dest: + ret void +} + +declare void @opaque() +declare void @llvm.objc.storeStrong(ptr, ptr) #0 +declare i32 @__CxxFrameHandler3(...) + +attributes #0 = { nounwind } +attributes #1 = { alwaysinline } + +; CHECK-LABEL: define void @test_catch_with_inline() +; ... +; CHECK: catch: +; CHECK-NEXT: %1 = catchpad within %0 [ptr null, i32 64, ptr %exn.slot] +; CHECK-NEXT: call void @llvm.objc.storeStrong(ptr %ex, ptr null) [ "funclet"(token %1) ] +; CHECK-NEXT: catchret from %1 to label %catchret.dest