From 252bda7ebd74ef6ad1478ec7114fc167ff63c0c4 Mon Sep 17 00:00:00 2001 From: Akira Hatanaka Date: Mon, 28 Jun 2021 11:02:30 -0700 Subject: [PATCH] [ObjC][ARC] Ignore operand bundle "clang.arc.attachedcall" on a call if the call's return type is void Instead of trying hard to prevent global optimization passes such as deadargelim from changing the return type to void, just ignore the bundle if the return type is void. clang currently emits calls to @llvm.objc.clang.arc.noop.use, which consumes the function call result, immediately after the function call to prevent changes to the return type, but optimization passes can delete the call to @llvm.objc.clang.arc.noop.use if the function call doesn't return, which enables deadargelim to change the return type. rdar://76671438 Differential Revision: https://reviews.llvm.org/D103062 --- docs/LangRef.rst | 6 ++++-- include/llvm/Analysis/ObjCARCUtil.h | 19 ++++++++++++++----- lib/IR/Verifier.cpp | 6 ++++-- test/Transforms/ObjCARC/contract-rv-attr.ll | 13 +++++++++++++ test/Verifier/operand-bundles.ll | 7 +++++++ 5 files changed, 42 insertions(+), 9 deletions(-) diff --git a/docs/LangRef.rst b/docs/LangRef.rst index 1986f232cc3..083ece60044 100644 --- a/docs/LangRef.rst +++ b/docs/LangRef.rst @@ -2406,8 +2406,10 @@ A ``"clang.arc.attachedcall`` operand bundle on a call indicates the call is 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/include/llvm/Analysis/ObjCARCUtil.h b/include/llvm/Analysis/ObjCARCUtil.h index 5d04ebadf08..2566bfbcf61 100644 --- a/include/llvm/Analysis/ObjCARCUtil.h +++ b/include/llvm/Analysis/ObjCARCUtil.h @@ -31,7 +31,21 @@ getAttachedCallOperandBundleEnum(bool IsRetain) { 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 @@ inline bool hasAttachedCallOpBundle(const CallBase *CB, bool IsRetain) { 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/lib/IR/Verifier.cpp b/lib/IR/Verifier.cpp index 468c935e3bb..24f5d513818 100644 --- a/lib/IR/Verifier.cpp +++ b/lib/IR/Verifier.cpp @@ -3353,9 +3353,11 @@ void Verifier::visitCallBase(CallBase &Call) { } 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/test/Transforms/ObjCARC/contract-rv-attr.ll b/test/Transforms/ObjCARC/contract-rv-attr.ll index 3a817327c36..18bc00b62db 100644 --- a/test/Transforms/ObjCARC/contract-rv-attr.ll +++ b/test/Transforms/ObjCARC/contract-rv-attr.ll @@ -55,9 +55,22 @@ cleanup: 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/test/Verifier/operand-bundles.ll b/test/Verifier/operand-bundles.ll index 4ef0e647988..d7d7b4f0f78 100644 --- a/test/Verifier/operand-bundles.ll +++ b/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 @@ define void @f_clang_arc_attachedcall() { ; 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 }