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
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.
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;
getAttachedCallOperandBundleEnum(IsRetain);
}
-inline bool hasAttachedCallOpBundle(const CallBase *CB) {
- return CB->getOperandBundle(LLVMContext::OB_clang_arc_attachedcall)
- .hasValue();
-}
-
} // end namespace objcarc
} // end namespace llvm
}
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
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"}
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.
; 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 }