diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -2406,8 +2406,10 @@ implicitly followed by a marker instruction and a call to an ObjC runtime function that uses the result of the call. If the argument passed to the operand bundle is 0, ``@objc_retainAutoreleasedReturnValue`` is called. If 1 is passed, -``@objc_unsafeClaimAutoreleasedReturnValue`` is called. A call with this bundle -implicitly uses its return value. +``@objc_unsafeClaimAutoreleasedReturnValue`` is called. The return value of a +call with this bundle is used by a call to ``@llvm.objc.clang.arc.noop.use`` +unless the called function's return type is void, in which case the operand +bundle is ignored. The operand bundle is needed to ensure the call is immediately followed by the marker instruction or the ObjC runtime call in the final output. diff --git a/llvm/include/llvm/Analysis/ObjCARCUtil.h b/llvm/include/llvm/Analysis/ObjCARCUtil.h --- a/llvm/include/llvm/Analysis/ObjCARCUtil.h +++ b/llvm/include/llvm/Analysis/ObjCARCUtil.h @@ -31,7 +31,21 @@ return IsRetain ? RVOB_Retain : RVOB_Claim; } +inline bool hasAttachedCallOpBundle(const CallBase *CB) { + // Ignore the bundle if the return type is void. Global optimization passes + // can turn the called function's return type to void. That should happen only + // if the call doesn't return and the call to @llvm.objc.clang.arc.noop.use + // no longer consumes the function return or is deleted. In that case, it's + // not necessary to emit the marker instruction or calls to the ARC runtime + // functions. + return !CB->getFunctionType()->getReturnType()->isVoidTy() && + CB->getOperandBundle(LLVMContext::OB_clang_arc_attachedcall) + .hasValue(); +} + inline bool hasAttachedCallOpBundle(const CallBase *CB, bool IsRetain) { + assert(hasAttachedCallOpBundle(CB) && + "call doesn't have operand bundle clang_arc_attachedcall"); auto B = CB->getOperandBundle(LLVMContext::OB_clang_arc_attachedcall); if (!B.hasValue()) return false; @@ -39,11 +53,6 @@ getAttachedCallOperandBundleEnum(IsRetain); } -inline bool hasAttachedCallOpBundle(const CallBase *CB) { - return CB->getOperandBundle(LLVMContext::OB_clang_arc_attachedcall) - .hasValue(); -} - } // end namespace objcarc } // end namespace llvm diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp --- a/llvm/lib/IR/Verifier.cpp +++ b/llvm/lib/IR/Verifier.cpp @@ -3353,9 +3353,11 @@ } if (FoundAttachedCallBundle) - Assert(FTy->getReturnType()->isPointerTy(), + Assert((FTy->getReturnType()->isPointerTy() || + (Call.doesNotReturn() && FTy->getReturnType()->isVoidTy())), "a call with operand bundle \"clang.arc.attachedcall\" must call a " - "function returning a pointer", + "function returning a pointer or a non-returning function that has " + "a void return type", Call); // Verify that each inlinable callsite of a debug-info-bearing function in a diff --git a/llvm/test/Transforms/ObjCARC/contract-rv-attr.ll b/llvm/test/Transforms/ObjCARC/contract-rv-attr.ll --- a/llvm/test/Transforms/ObjCARC/contract-rv-attr.ll +++ b/llvm/test/Transforms/ObjCARC/contract-rv-attr.ll @@ -55,9 +55,22 @@ ret i8* %retval.0 } +; CHECK-LABEL: define void @test3( +; CHECK: call void @foo2() #[[ATTR1:.*]] [ "clang.arc.attachedcall"(i64 0) ] +; CHECK-NEXT: ret void + +define void @test3() { + call void @foo2() #0 [ "clang.arc.attachedcall"(i64 0) ] + ret void +} + declare i8* @foo() +declare void @foo2() declare i32 @__gxx_personality_v0(...) !llvm.module.flags = !{!0} +; CHECK: attributes #[[ATTR1]] = { noreturn } +attributes #0 = { noreturn } + !0 = !{i32 1, !"clang.arc.retainAutoreleasedReturnValueMarker", !"mov\09fp, fp\09\09// marker for objc_retainAutoreleaseReturnValue"} diff --git a/llvm/test/Verifier/operand-bundles.ll b/llvm/test/Verifier/operand-bundles.ll --- a/llvm/test/Verifier/operand-bundles.ll +++ b/llvm/test/Verifier/operand-bundles.ll @@ -4,6 +4,7 @@ declare void @g() declare %0* @foo0() declare i8 @foo1() +declare void @noreturn_func() ; Operand bundles uses are like regular uses, and need to be dominated ; by their defs. @@ -69,9 +70,15 @@ ; CHECK-NEXT: call %0* @foo0() [ "clang.arc.attachedcall"(i64 0), "clang.arc.attachedcall"(i64 0) ] ; CHECK-NEXT: must call a function returning a pointer ; CHECK-NEXT: call i8 @foo1() [ "clang.arc.attachedcall"(i64 0) ] +; CHECK-NEXT: or a non-returning function +; CHECK-NEXT: call void @g() [ "clang.arc.attachedcall"(i64 0) ] call %0* @foo0() [ "clang.arc.attachedcall"(i64 0) ] call %0* @foo0() [ "clang.arc.attachedcall"(i64 0), "clang.arc.attachedcall"(i64 0) ] call i8 @foo1() [ "clang.arc.attachedcall"(i64 0) ] + call void @noreturn_func() #0 [ "clang.arc.attachedcall"(i64 0) ] + call void @g() [ "clang.arc.attachedcall"(i64 0) ] ret void } + +attributes #0 = { noreturn }