diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -2390,10 +2390,12 @@ 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. +function that uses the result of the call. The operand of the bundle is either +the pointer to the runtime function (``@objc_retainAutoreleasedReturnValue`` or +``@objc_unsafeClaimAutoreleasedReturnValue``) or null. A null operand indicates +the marker instruction has to be emitted after the call but calls to the runtime +functions don't have to be emitted since they already have been emitted. A call +with this bundle implicitly uses its return value. 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 @@ -14,6 +14,7 @@ #ifndef LLVM_IR_OBJCARCUTIL_H #define LLVM_IR_OBJCARCUTIL_H +#include "llvm/Analysis/ObjCARCInstKind.h" #include "llvm/IR/InstrTypes.h" #include "llvm/IR/LLVMContext.h" @@ -24,19 +25,32 @@ return "clang.arc.retainAutoreleasedReturnValueMarker"; } -enum AttachedCallOperandBundle : unsigned { RVOB_Retain, RVOB_Claim }; +/// This function checks whether a call has operand bundle +/// clang_arc_attachedcall and returns the pointer to the ARC runtime function. +inline std::pair getAttachedARCFunction(const CallBase *CB) { + auto B = CB->getOperandBundle(LLVMContext::OB_clang_arc_attachedcall); + if (!B.hasValue()) + return {false, nullptr}; + + if (B->Inputs.size() == 0) + return {true, nullptr}; -inline AttachedCallOperandBundle -getAttachedCallOperandBundleEnum(bool IsRetain) { - return IsRetain ? RVOB_Retain : RVOB_Claim; + return {true, cast(B->Inputs[0])}; } -inline bool hasAttachedCallOpBundle(const CallBase *CB, bool IsRetain) { - auto B = CB->getOperandBundle(LLVMContext::OB_clang_arc_attachedcall); - if (!B.hasValue()) - return false; - return cast(B->Inputs[0])->getZExtValue() == - getAttachedCallOperandBundleEnum(IsRetain); +/// This function returns the ARCInstKind of the function attached to operand +/// bundle clang_arc_attachedcall. It returns None if the call doesn't have the +/// operand bundle or the operand is null. Otherwise it returns either RetainRV +/// or ClaimRV. +inline ARCInstKind getAttachedARCFunctionKind(const CallBase *CB) { + std::pair P = getAttachedARCFunction(CB); + if (!P.first || !P.second) + return ARCInstKind::None; + auto FnClass = GetFunctionClass(P.second); + assert( + (FnClass == ARCInstKind::RetainRV || FnClass == ARCInstKind::ClaimRV) && + "unexpected ARC runtime function"); + return FnClass; } inline bool hasAttachedCallOpBundle(const CallBase *CB) { 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 @@ -2610,9 +2610,12 @@ // direct call/invokes, never having its "address taken". // Only do this if the module is materialized, otherwise we don't have all the // uses. - if (F.getIntrinsicID() && F.getParent()->isMaterialized()) { + Intrinsic::ID IID = F.getIntrinsicID(); + if (IID && F.getParent()->isMaterialized()) { const User *U; - if (F.hasAddressTaken(&U)) + if (F.hasAddressTaken(&U) && + IID != Intrinsic::objc_retainAutoreleasedReturnValue && + IID != Intrinsic::objc_unsafeClaimAutoreleasedReturnValue) Assert(false, "Invalid user of intrinsic instruction!", U); } @@ -4437,10 +4440,26 @@ } if (Function *F = dyn_cast(I.getOperand(i))) { + // This code checks whether the function is a retainRV/claimRV intrinsic + // whose address is used as the operand of a clang_arc_attachedcall + // operand bundle. + auto IsAttachedCallOperand = [](Function *F, const CallBase *CBI, + int Idx) { + Intrinsic::ID IID = F->getIntrinsicID(); + return CBI && + (IID == Intrinsic::objc_retainAutoreleasedReturnValue || + IID == Intrinsic::objc_unsafeClaimAutoreleasedReturnValue) && + CBI->isBundleOperand(Idx) && + CBI->getOperandBundleForOperand(Idx).getTagID() == + LLVMContext::OB_clang_arc_attachedcall; + }; + // Check to make sure that the "address of" an intrinsic function is never - // taken. - Assert(!F->isIntrinsic() || - (CBI && &CBI->getCalledOperandUse() == &I.getOperandUse(i)), + // taken except when the intrinsic is objc_retainAutoreleasedReturnValue + // or objc_unsafeClaimAutoreleasedReturnValue. + Assert((!F->isIntrinsic() || + (CBI && &CBI->getCalledOperandUse() == &I.getOperandUse(i)) || + IsAttachedCallOperand(F, CBI, i)), "Cannot take the address of an intrinsic!", &I); Assert( !F->isIntrinsic() || isa(I) || @@ -4454,7 +4473,8 @@ F->getIntrinsicID() == Intrinsic::experimental_patchpoint_void || F->getIntrinsicID() == Intrinsic::experimental_patchpoint_i64 || F->getIntrinsicID() == Intrinsic::experimental_gc_statepoint || - F->getIntrinsicID() == Intrinsic::wasm_rethrow, + F->getIntrinsicID() == Intrinsic::wasm_rethrow || + IsAttachedCallOperand(F, CBI, i), "Cannot invoke an intrinsic other than donothing, patchpoint, " "statepoint, coro_resume or coro_destroy", &I); diff --git a/llvm/lib/Target/X86/X86ISelLowering.cpp b/llvm/lib/Target/X86/X86ISelLowering.cpp --- a/llvm/lib/Target/X86/X86ISelLowering.cpp +++ b/llvm/lib/Target/X86/X86ISelLowering.cpp @@ -4443,8 +4443,15 @@ // target. RuntimeCallType == 0 selects objc_retainAutoreleasedReturnValue, // RuntimeCallType == 0 selects objc_unsafeClaimAutoreleasedReturnValue when // epxanding the pseudo. + Function *ARCFn = objcarc::getAttachedARCFunction(CLI.CB).second; + Module *M = MF.getFunction().getParent(); + assert( + ARCFn && + (ARCFn == M->getFunction("objc_retainAutoreleasedReturnValue") || + ARCFn == M->getFunction("objc_unsafeClaimAutoreleasedReturnValue")) && + "unexpected ARC function"); unsigned RuntimeCallType = - objcarc::hasAttachedCallOpBundle(CLI.CB, true) ? 0 : 1; + ARCFn == M->getFunction("objc_retainAutoreleasedReturnValue") ? 0 : 1; Ops.insert(Ops.begin() + 1, DAG.getTargetConstant(RuntimeCallType, dl, MVT::i32)); Chain = DAG.getNode(X86ISD::CALL_RVMARKER, dl, NodeTys, Ops); diff --git a/llvm/lib/Transforms/ObjCARC/ObjCARC.h b/llvm/lib/Transforms/ObjCARC/ObjCARC.h --- a/llvm/lib/Transforms/ObjCARC/ObjCARC.h +++ b/llvm/lib/Transforms/ObjCARC/ObjCARC.h @@ -105,8 +105,7 @@ class BundledRetainClaimRVs { public: - BundledRetainClaimRVs(ARCRuntimeEntryPoints &P, bool ContractPass) - : EP(P), ContractPass(ContractPass) {} + BundledRetainClaimRVs(bool ContractPass) : ContractPass(ContractPass) {} ~BundledRetainClaimRVs(); /// Insert a retainRV/claimRV call to the normal destination blocks of invokes @@ -155,7 +154,6 @@ /// A map of inserted retainRV/claimRV calls to annotated calls/invokes. DenseMap RVCalls; - ARCRuntimeEntryPoints &EP; bool ContractPass; }; diff --git a/llvm/lib/Transforms/ObjCARC/ObjCARC.cpp b/llvm/lib/Transforms/ObjCARC/ObjCARC.cpp --- a/llvm/lib/Transforms/ObjCARC/ObjCARC.cpp +++ b/llvm/lib/Transforms/ObjCARC/ObjCARC.cpp @@ -103,9 +103,8 @@ Instruction *InsertPt, CallBase *AnnotatedCall, const DenseMap &BlockColors) { IRBuilder<> Builder(InsertPt); - bool IsRetainRV = objcarc::hasAttachedCallOpBundle(AnnotatedCall, true); - Function *Func = EP.get(IsRetainRV ? ARCRuntimeEntryPointKind::RetainRV - : ARCRuntimeEntryPointKind::ClaimRV); + Function *Func = objcarc::getAttachedARCFunction(AnnotatedCall).second; + assert(Func && "operand isn't a Function"); Type *ParamTy = Func->getArg(0)->getType(); Value *CallArg = Builder.CreateBitCast(AnnotatedCall, ParamTy); auto *Call = @@ -116,13 +115,21 @@ BundledRetainClaimRVs::~BundledRetainClaimRVs() { if (ContractPass) { - // At this point, we know that the annotated calls can't be tail calls as - // they are followed by marker instructions and retainRV/claimRV calls. Mark - // them as notail, so that the backend knows these calls can't be tail - // calls. - for (auto P : RVCalls) - if (auto *CI = dyn_cast(P.second)) + for (auto P : RVCalls) { + CallBase *CB = P.second; + // At this point, we know that the annotated calls can't be tail calls + // as they are followed by marker instructions and retainRV/claimRV + // calls. Mark them as notail so that the backend knows these calls + // can't be tail calls. + if (auto *CI = dyn_cast(CB)) CI->setTailCallKind(CallInst::TCK_NoTail); + + // Remove the ARC intrinsic function operand from the operand bundle. + OperandBundleDef OB("clang.arc.attachedcall", None); + auto *NewCB = CallBase::Create(CB, OB, CB); + CB->replaceAllUsesWith(NewCB); + CB->eraseFromParent(); + } } else { for (auto P : RVCalls) EraseInstruction(P.first); diff --git a/llvm/lib/Transforms/ObjCARC/ObjCARCContract.cpp b/llvm/lib/Transforms/ObjCARC/ObjCARCContract.cpp --- a/llvm/lib/Transforms/ObjCARC/ObjCARCContract.cpp +++ b/llvm/lib/Transforms/ObjCARC/ObjCARCContract.cpp @@ -537,7 +537,7 @@ AA = A; DT = D; PA.setAA(A); - BundledRetainClaimRVs BRV(EP, true); + BundledRetainClaimRVs BRV(true); BundledInsts = &BRV; std::pair R = BundledInsts->insertAfterInvokes(F, DT); diff --git a/llvm/lib/Transforms/ObjCARC/ObjCARCOpts.cpp b/llvm/lib/Transforms/ObjCARC/ObjCARCOpts.cpp --- a/llvm/lib/Transforms/ObjCARC/ObjCARCOpts.cpp +++ b/llvm/lib/Transforms/ObjCARC/ObjCARCOpts.cpp @@ -2404,7 +2404,7 @@ return false; Changed = CFGChanged = false; - BundledRetainClaimRVs BRV(EP, false); + BundledRetainClaimRVs BRV(false); BundledInsts = &BRV; LLVM_DEBUG(dbgs() << "<<< ObjCARCOpt: Visiting Function: " << F.getName() 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 @@ -1669,10 +1669,13 @@ /// 3. Otherwise, a call to objc_retain is inserted if the call in the caller is /// a retainRV call. static void -inlineRetainOrClaimRVCalls(CallBase &CB, +inlineRetainOrClaimRVCalls(CallBase &CB, objcarc::ARCInstKind RVCallKind, const SmallVectorImpl &Returns) { Module *Mod = CB.getModule(); - bool IsRetainRV = objcarc::hasAttachedCallOpBundle(&CB, true), + assert(((RVCallKind == objcarc::ARCInstKind::RetainRV) || + (RVCallKind == objcarc::ARCInstKind::ClaimRV)) && + "unexpected ARC function"); + bool IsRetainRV = RVCallKind == objcarc::ARCInstKind::RetainRV, IsClaimRV = !IsRetainRV; for (auto *RI : Returns) { @@ -1716,9 +1719,7 @@ !objcarc::hasAttachedCallOpBundle(CI)) { // If we've found an unannotated call that defines RetOpnd, add a // "clang.arc.attachedcall" operand bundle. - Value *BundleArgs[] = {ConstantInt::get( - Builder.getInt64Ty(), - objcarc::getAttachedCallOperandBundleEnum(IsRetainRV))}; + Value *BundleArgs[] = {objcarc::getAttachedARCFunction(&CB).second}; OperandBundleDef OB("clang.arc.attachedcall", BundleArgs); auto *NewCall = CallBase::addOperandBundle( CI, LLVMContext::OB_clang_arc_attachedcall, OB, CI); @@ -1950,8 +1951,9 @@ FirstNewBlock = LastBlock; ++FirstNewBlock; // Insert retainRV/clainRV runtime calls. - if (objcarc::hasAttachedCallOpBundle(&CB)) - inlineRetainOrClaimRVCalls(CB, Returns); + objcarc::ARCInstKind RVCallKind = objcarc::getAttachedARCFunctionKind(&CB); + if (RVCallKind != objcarc::ARCInstKind::None) + inlineRetainOrClaimRVCalls(CB, RVCallKind, Returns); // Updated caller/callee profiles only when requested. For sample loader // inlining, the context-sensitive inlinee profile doesn't need to be diff --git a/llvm/test/CodeGen/AArch64/call-rv-marker.ll b/llvm/test/CodeGen/AArch64/call-rv-marker.ll --- a/llvm/test/CodeGen/AArch64/call-rv-marker.ll +++ b/llvm/test/CodeGen/AArch64/call-rv-marker.ll @@ -33,7 +33,7 @@ ; GISEL-NOT: mov x29, x29 ; entry: - %call = call i8* @foo1() [ "clang.arc.attachedcall"(i64 0) ] + %call = call i8* @foo1() [ "clang.arc.attachedcall"() ] ret i8* %call } @@ -49,7 +49,7 @@ entry: %tobool.not = icmp eq i32 %c, 0 %.sink = select i1 %tobool.not, i32 2, i32 1 - %call1 = call i8* @foo0(i32 %.sink) [ "clang.arc.attachedcall"(i64 0) ] + %call1 = call i8* @foo0(i32 %.sink) [ "clang.arc.attachedcall"() ] tail call void @foo2(i8* %call1) ret void } @@ -61,7 +61,7 @@ ; SELDAG-NEXT: mov x29, x29 ; entry: - %call = call i8* @foo1() [ "clang.arc.attachedcall"(i64 0) ] + %call = call i8* @foo1() [ "clang.arc.attachedcall"() ] invoke void @objc_object(i8* %call) #5 to label %invoke.cont unwind label %lpad @@ -87,7 +87,7 @@ %s = alloca %struct.S, align 1 %0 = getelementptr inbounds %struct.S, %struct.S* %s, i64 0, i32 0 call void @llvm.lifetime.start.p0i8(i64 1, i8* nonnull %0) #2 - %call = invoke i8* @foo1() [ "clang.arc.attachedcall"(i64 0) ] + %call = invoke i8* @foo1() [ "clang.arc.attachedcall"() ] to label %invoke.cont unwind label %lpad invoke.cont: ; preds = %entry @@ -127,7 +127,7 @@ ; entry: %0 = load i8* ()*, i8* ()** @fptr, align 8 - %call = call i8* %0() [ "clang.arc.attachedcall"(i64 0) ] + %call = call i8* %0() [ "clang.arc.attachedcall"() ] tail call void @foo2(i8* %call) ret i8* %call } @@ -142,7 +142,7 @@ ; CHECK-NEXT: bl foo ; SELDAG-NEXT: mov x29, x29 ; GISEL-NOT: mov x29, x29 - call i8* @foo(i64 %c, i64 %b, i64 %a) [ "clang.arc.attachedcall"(i64 0) ] + call i8* @foo(i64 %c, i64 %b, i64 %a) [ "clang.arc.attachedcall"() ] ret void } diff --git a/llvm/test/CodeGen/X86/call-rv-marker.ll b/llvm/test/CodeGen/X86/call-rv-marker.ll --- a/llvm/test/CodeGen/X86/call-rv-marker.ll +++ b/llvm/test/CodeGen/X86/call-rv-marker.ll @@ -34,7 +34,7 @@ ; CHECK-NEXT: retq ; entry: - %call = call i8* @foo1() [ "clang.arc.attachedcall"(i64 0) ] + %call = call i8* @foo1() [ "clang.arc.attachedcall"(i8* (i8*)* @objc_retainAutoreleasedReturnValue) ] ret i8* %call } @@ -49,7 +49,7 @@ ; CHECK-NEXT: retq ; entry: - %call = call i8* @foo1() [ "clang.arc.attachedcall"(i64 1) ] + %call = call i8* @foo1() [ "clang.arc.attachedcall"(i8* (i8*)* @objc_unsafeClaimAutoreleasedReturnValue) ] ret i8* %call } @@ -70,7 +70,7 @@ entry: %tobool.not = icmp eq i32 %c, 0 %.sink = select i1 %tobool.not, i32 2, i32 1 - %call1 = call i8* @foo0(i32 %.sink) [ "clang.arc.attachedcall"(i64 0) ] + %call1 = call i8* @foo0(i32 %.sink) [ "clang.arc.attachedcall"(i8* (i8*)* @objc_retainAutoreleasedReturnValue) ] tail call void @foo2(i8* %call1) ret void } @@ -92,7 +92,7 @@ ; CHECK-NEXT: Ltmp0: ; entry: - %call = call i8* @foo1() [ "clang.arc.attachedcall"(i64 0) ] + %call = call i8* @foo1() [ "clang.arc.attachedcall"(i8* (i8*)* @objc_retainAutoreleasedReturnValue) ] invoke void @objc_object(i8* %call) #5 to label %invoke.cont unwind label %lpad @@ -127,7 +127,7 @@ %s = alloca %struct.S, align 1 %0 = getelementptr inbounds %struct.S, %struct.S* %s, i64 0, i32 0 call void @llvm.lifetime.start.p0i8(i64 1, i8* nonnull %0) #2 - %call = invoke i8* @foo1() [ "clang.arc.attachedcall"(i64 0) ] + %call = invoke i8* @foo1() [ "clang.arc.attachedcall"(i8* (i8*)* @objc_retainAutoreleasedReturnValue) ] to label %invoke.cont unwind label %lpad invoke.cont: ; preds = %entry @@ -177,7 +177,7 @@ ; entry: %lv = load i8* ()*, i8* ()** @fptr, align 8 - %call = call i8* %lv() [ "clang.arc.attachedcall"(i64 0) ] + %call = call i8* %lv() [ "clang.arc.attachedcall"(i8* (i8*)* @objc_retainAutoreleasedReturnValue) ] tail call void @foo2(i8* %call) ret i8* %call } @@ -197,7 +197,7 @@ ; CHECK-NEXT: popq %rax ; CHECK-NEXT: retq ; - %r = call i8* @foo(i64 %c, i64 %b, i64 %a) [ "clang.arc.attachedcall"(i64 0) ] + %r = call i8* @foo(i64 %c, i64 %b, i64 %a) [ "clang.arc.attachedcall"(i8* (i8*)* @objc_retainAutoreleasedReturnValue) ] ret void } @@ -210,10 +210,12 @@ ; CHECK-NEXT: movq %rax, %rdi ; CHECK-NEXT: callq _objc_retainAutoreleasedReturnValue ; - %call1 = notail call i8* @foo_nonlazybind() [ "clang.arc.attachedcall"(i64 0) ] + %call1 = notail call i8* @foo_nonlazybind() [ "clang.arc.attachedcall"(i8* (i8*)* @objc_retainAutoreleasedReturnValue) ] ret void } declare i8* @foo_nonlazybind() nonlazybind +declare i8* @objc_retainAutoreleasedReturnValue(i8*) +declare i8* @objc_unsafeClaimAutoreleasedReturnValue(i8*) declare i32 @__gxx_personality_v0(...) diff --git a/llvm/test/Transforms/DeadArgElim/deadretval.ll b/llvm/test/Transforms/DeadArgElim/deadretval.ll --- a/llvm/test/Transforms/DeadArgElim/deadretval.ll +++ b/llvm/test/Transforms/DeadArgElim/deadretval.ll @@ -32,10 +32,12 @@ declare void @llvm.objc.clang.arc.noop.use(...) ; CHECK-LABEL: define i8* @test4( -; CHECK: tail call i8* @callee4() [ "clang.arc.attachedcall"(i64 0) ] +; CHECK: tail call i8* @callee4() [ "clang.arc.attachedcall"(i8* (i8*)* @llvm.objc.retainAutoreleasedReturnValue) ] define i8* @test4() { - %call = tail call i8* @callee4(i8* @g0) [ "clang.arc.attachedcall"(i64 0) ] + %call = tail call i8* @callee4(i8* @g0) [ "clang.arc.attachedcall"(i8* (i8*)* @llvm.objc.retainAutoreleasedReturnValue) ] call void (...) @llvm.objc.clang.arc.noop.use(i8* %call) ret i8* @g0 } + +declare i8* @llvm.objc.retainAutoreleasedReturnValue(i8*) diff --git a/llvm/test/Transforms/Inline/inline-retainRV-call.ll b/llvm/test/Transforms/Inline/inline-retainRV-call.ll --- a/llvm/test/Transforms/Inline/inline-retainRV-call.ll +++ b/llvm/test/Transforms/Inline/inline-retainRV-call.ll @@ -4,35 +4,35 @@ declare i8* @foo0() define i8* @callee0_autoreleaseRV() { - %call = call i8* @foo0() [ "clang.arc.attachedcall"(i64 0) ] + %call = call i8* @foo0() [ "clang.arc.attachedcall"(i8* (i8*)* @llvm.objc.retainAutoreleasedReturnValue) ] %1 = tail call i8* @llvm.objc.autoreleaseReturnValue(i8* %call) ret i8* %call } ; CHECK-LABEL: define void @test0_autoreleaseRV( -; CHECK: call i8* @foo0() [ "clang.arc.attachedcall"(i64 0) ] +; CHECK: call i8* @foo0() [ "clang.arc.attachedcall"(i8* (i8*)* @llvm.objc.retainAutoreleasedReturnValue) ] define void @test0_autoreleaseRV() { - %call = call i8* @callee0_autoreleaseRV() [ "clang.arc.attachedcall"(i64 0) ] + %call = call i8* @callee0_autoreleaseRV() [ "clang.arc.attachedcall"(i8* (i8*)* @llvm.objc.retainAutoreleasedReturnValue) ] ret void } ; CHECK-LABEL: define void @test0_claimRV_autoreleaseRV( -; CHECK: %[[CALL:.*]] = call i8* @foo0() [ "clang.arc.attachedcall"(i64 0) ] +; CHECK: %[[CALL:.*]] = call i8* @foo0() [ "clang.arc.attachedcall"(i8* (i8*)* @llvm.objc.retainAutoreleasedReturnValue) ] ; CHECK: call void @llvm.objc.release(i8* %[[CALL]]) ; CHECK-NEXT: ret void define void @test0_claimRV_autoreleaseRV() { - %call = call i8* @callee0_autoreleaseRV() [ "clang.arc.attachedcall"(i64 1) ] + %call = call i8* @callee0_autoreleaseRV() [ "clang.arc.attachedcall"(i8* (i8*)* @llvm.objc.unsafeClaimAutoreleasedReturnValue) ] ret void } ; CHECK-LABEL: define void @test1_autoreleaseRV( -; CHECK: invoke i8* @foo0() [ "clang.arc.attachedcall"(i64 0) ] +; CHECK: invoke i8* @foo0() [ "clang.arc.attachedcall"(i8* (i8*)* @llvm.objc.retainAutoreleasedReturnValue) ] define void @test1_autoreleaseRV() personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) { entry: - %call = invoke i8* @callee0_autoreleaseRV() [ "clang.arc.attachedcall"(i64 0) ] + %call = invoke i8* @callee0_autoreleaseRV() [ "clang.arc.attachedcall"(i8* (i8*)* @llvm.objc.retainAutoreleasedReturnValue) ] to label %invoke.cont unwind label %lpad invoke.cont: @@ -45,13 +45,13 @@ } ; CHECK-LABEL: define void @test1_claimRV_autoreleaseRV( -; CHECK: %[[INVOKE:.*]] = invoke i8* @foo0() [ "clang.arc.attachedcall"(i64 0) ] +; CHECK: %[[INVOKE:.*]] = invoke i8* @foo0() [ "clang.arc.attachedcall"(i8* (i8*)* @llvm.objc.retainAutoreleasedReturnValue) ] ; CHECK: call void @llvm.objc.release(i8* %[[INVOKE]]) ; CHECK-NEXT: br define void @test1_claimRV_autoreleaseRV() personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) { entry: - %call = invoke i8* @callee0_autoreleaseRV() [ "clang.arc.attachedcall"(i64 1) ] + %call = invoke i8* @callee0_autoreleaseRV() [ "clang.arc.attachedcall"(i8* (i8*)* @llvm.objc.unsafeClaimAutoreleasedReturnValue) ] to label %invoke.cont unwind label %lpad invoke.cont: @@ -69,29 +69,29 @@ } ; CHECK-LABEL: define void @test2_no_autoreleaseRV( -; CHECK: call i8* @foo0() [ "clang.arc.attachedcall"(i64 0) ] +; CHECK: call i8* @foo0() [ "clang.arc.attachedcall"(i8* (i8*)* @llvm.objc.retainAutoreleasedReturnValue) ] ; CHECK-NEXT: ret void define void @test2_no_autoreleaseRV() { - %call = call i8* @callee1_no_autoreleaseRV() [ "clang.arc.attachedcall"(i64 0) ] + %call = call i8* @callee1_no_autoreleaseRV() [ "clang.arc.attachedcall"(i8* (i8*)* @llvm.objc.retainAutoreleasedReturnValue) ] ret void } ; CHECK-LABEL: define void @test2_claimRV_no_autoreleaseRV( -; CHECK: call i8* @foo0() [ "clang.arc.attachedcall"(i64 1) ] +; CHECK: call i8* @foo0() [ "clang.arc.attachedcall"(i8* (i8*)* @llvm.objc.unsafeClaimAutoreleasedReturnValue) ] ; CHECK-NEXT: ret void define void @test2_claimRV_no_autoreleaseRV() { - %call = call i8* @callee1_no_autoreleaseRV() [ "clang.arc.attachedcall"(i64 1) ] + %call = call i8* @callee1_no_autoreleaseRV() [ "clang.arc.attachedcall"(i8* (i8*)* @llvm.objc.unsafeClaimAutoreleasedReturnValue) ] ret void } ; CHECK-LABEL: define void @test3_no_autoreleaseRV( -; CHECK: invoke i8* @foo0() [ "clang.arc.attachedcall"(i64 0) ] +; CHECK: invoke i8* @foo0() [ "clang.arc.attachedcall"(i8* (i8*)* @llvm.objc.retainAutoreleasedReturnValue) ] define void @test3_no_autoreleaseRV() personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) { entry: - %call = invoke i8* @callee1_no_autoreleaseRV() [ "clang.arc.attachedcall"(i64 0) ] + %call = invoke i8* @callee1_no_autoreleaseRV() [ "clang.arc.attachedcall"(i8* (i8*)* @llvm.objc.retainAutoreleasedReturnValue) ] to label %invoke.cont unwind label %lpad invoke.cont: @@ -117,7 +117,7 @@ ; CHECK-NEXT: ret void define void @test4_nocall() { - %call = call i8* @callee2_nocall() [ "clang.arc.attachedcall"(i64 0) ] + %call = call i8* @callee2_nocall() [ "clang.arc.attachedcall"(i8* (i8*)* @llvm.objc.retainAutoreleasedReturnValue) ] ret void } @@ -126,7 +126,7 @@ ; CHECK-NEXT: ret void define void @test4_claimRV_nocall() { - %call = call i8* @callee2_nocall() [ "clang.arc.attachedcall"(i64 1) ] + %call = call i8* @callee2_nocall() [ "clang.arc.attachedcall"(i8* (i8*)* @llvm.objc.unsafeClaimAutoreleasedReturnValue) ] ret void } @@ -134,17 +134,17 @@ ; the attribute. I'm not sure this will happen in practice. define i8* @callee3_marker() { - %1 = call i8* @foo0() [ "clang.arc.attachedcall"(i64 0) ] + %1 = call i8* @foo0() [ "clang.arc.attachedcall"(i8* (i8*)* @llvm.objc.retainAutoreleasedReturnValue) ] ret i8* %1 } ; CHECK-LABEL: define void @test5( -; CHECK: %[[V0:.*]] = call i8* @foo0() [ "clang.arc.attachedcall"(i64 0) ] +; CHECK: %[[V0:.*]] = call i8* @foo0() [ "clang.arc.attachedcall"(i8* (i8*)* @llvm.objc.retainAutoreleasedReturnValue) ] ; CHECK-NEXT: call i8* @llvm.objc.retain(i8* %[[V0]]) ; CHECK-NEXT: ret void define void @test5() { - %call = call i8* @callee3_marker() [ "clang.arc.attachedcall"(i64 0) ] + %call = call i8* @callee3_marker() [ "clang.arc.attachedcall"(i8* (i8*)* @llvm.objc.retainAutoreleasedReturnValue) ] ret void } @@ -153,23 +153,25 @@ ; autoreleaseRV that isn't a cast instruction. define i8* @callee0_autoreleaseRV2() { - %call = call i8* @foo0() [ "clang.arc.attachedcall"(i64 0) ] + %call = call i8* @foo0() [ "clang.arc.attachedcall"(i8* (i8*)* @llvm.objc.retainAutoreleasedReturnValue) ] %1 = tail call i8* @llvm.objc.autoreleaseReturnValue(i8* %call) store i8* null, i8** @g0 ret i8* %call } ; CHECK-LABEL: define void @test6( -; CHECK: %[[V0:.*]] = call i8* @foo0() [ "clang.arc.attachedcall"(i64 0) ] +; CHECK: %[[V0:.*]] = call i8* @foo0() [ "clang.arc.attachedcall"(i8* (i8*)* @llvm.objc.retainAutoreleasedReturnValue) ] ; CHECK: call i8* @llvm.objc.autoreleaseReturnValue(i8* %[[V0]]) ; CHECK: store i8* null, i8** @g0, align 8 ; CHECK: call i8* @llvm.objc.retain(i8* %[[V0]]) ; CHECK-NEXT: ret void define void @test6() { - %call = call i8* @callee0_autoreleaseRV2() [ "clang.arc.attachedcall"(i64 0) ] + %call = call i8* @callee0_autoreleaseRV2() [ "clang.arc.attachedcall"(i8* (i8*)* @llvm.objc.retainAutoreleasedReturnValue) ] ret void } +declare i8* @llvm.objc.retainAutoreleasedReturnValue(i8*) +declare i8* @llvm.objc.unsafeClaimAutoreleasedReturnValue(i8*) declare i8* @llvm.objc.autoreleaseReturnValue(i8*) declare i32 @__gxx_personality_v0(...) diff --git a/llvm/test/Transforms/ObjCARC/contract-marker-funclet.ll b/llvm/test/Transforms/ObjCARC/contract-marker-funclet.ll --- a/llvm/test/Transforms/ObjCARC/contract-marker-funclet.ll +++ b/llvm/test/Transforms/ObjCARC/contract-marker-funclet.ll @@ -51,11 +51,11 @@ } ; CHECK-LABEL: define dso_local void @"?test_attr_claimRV@@YAXXZ"() -; CHECK: %[[CALL4:.*]] = notail call i8* @"?noexcept_func@@YAPAUobjc_object@@XZ"() [ "clang.arc.attachedcall"(i64 1) ] +; CHECK: %[[CALL4:.*]] = notail call i8* @"?noexcept_func@@YAPAUobjc_object@@XZ"() [ "clang.arc.attachedcall"() ] ; CHECK: call i8* @llvm.objc.unsafeClaimAutoreleasedReturnValue(i8* %[[CALL4]]) ; CHECK: %[[V1:.*]] = cleanuppad -; CHECK: %[[CALL:.*]] = notail call i8* @"?noexcept_func@@YAPAUobjc_object@@XZ"() [ "funclet"(token %[[V1]]), "clang.arc.attachedcall"(i64 1) ] +; CHECK: %[[CALL:.*]] = notail call i8* @"?noexcept_func@@YAPAUobjc_object@@XZ"() [ "funclet"(token %[[V1]]), "clang.arc.attachedcall"() ] ; CHECK: call i8* @llvm.objc.unsafeClaimAutoreleasedReturnValue(i8* %[[CALL]]) [ "funclet"(token %[[V1]]) ] define dso_local void @"?test_attr_claimRV@@YAXXZ"() local_unnamed_addr #0 personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) { @@ -64,12 +64,12 @@ to label %invoke.cont unwind label %ehcleanup invoke.cont: ; preds = %entry - %call.i4 = tail call i8* @"?noexcept_func@@YAPAUobjc_object@@XZ"() #2 [ "clang.arc.attachedcall"(i64 1) ] + %call.i4 = tail call i8* @"?noexcept_func@@YAPAUobjc_object@@XZ"() #2 [ "clang.arc.attachedcall"(i8* (i8*)* @llvm.objc.unsafeClaimAutoreleasedReturnValue) ] ret void ehcleanup: ; preds = %entry %0 = cleanuppad within none [] - %call.i = call i8* @"?noexcept_func@@YAPAUobjc_object@@XZ"() #2 [ "funclet"(token %0), "clang.arc.attachedcall"(i64 1) ] + %call.i = call i8* @"?noexcept_func@@YAPAUobjc_object@@XZ"() #2 [ "funclet"(token %0), "clang.arc.attachedcall"(i8* (i8*)* @llvm.objc.unsafeClaimAutoreleasedReturnValue) ] cleanupret from %0 unwind to caller } 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 @@ -2,30 +2,30 @@ ; RUN: opt -passes=objc-arc-contract -S < %s | FileCheck %s ; CHECK-LABEL: define void @test0() { -; CHECK: %[[CALL:.*]] = notail call i8* @foo() [ "clang.arc.attachedcall"(i64 0) ] +; CHECK: %[[CALL:.*]] = notail call i8* @foo() [ "clang.arc.attachedcall"() ] ; CHECK: call i8* @llvm.objc.retainAutoreleasedReturnValue(i8* %[[CALL]]) define void @test0() { - %call1 = call i8* @foo() [ "clang.arc.attachedcall"(i64 0) ] + %call1 = call i8* @foo() [ "clang.arc.attachedcall"(i8* (i8*)* @llvm.objc.retainAutoreleasedReturnValue) ] ret void } ; CHECK-LABEL: define void @test1() { -; CHECK: %[[CALL:.*]] = notail call i8* @foo() [ "clang.arc.attachedcall"(i64 1) ] +; CHECK: %[[CALL:.*]] = notail call i8* @foo() [ "clang.arc.attachedcall"() ] ; CHECK: call i8* @llvm.objc.unsafeClaimAutoreleasedReturnValue(i8* %[[CALL]]) define void @test1() { - %call1 = call i8* @foo() [ "clang.arc.attachedcall"(i64 1) ] + %call1 = call i8* @foo() [ "clang.arc.attachedcall"(i8* (i8*)* @llvm.objc.unsafeClaimAutoreleasedReturnValue) ] ret void } ; CHECK-LABEL:define i8* @test2( -; CHECK: %[[CALL1:.*]] = invoke i8* @foo() [ "clang.arc.attachedcall"(i64 0) ] +; CHECK: %[[CALL1:.*]] = invoke i8* @foo() [ "clang.arc.attachedcall"() ] ; CHECK: %[[V0:.*]] = call i8* @llvm.objc.retainAutoreleasedReturnValue(i8* %[[CALL1]]) ; CHECK-NEXT: br -; CHECK: %[[CALL3:.*]] = invoke i8* @foo() [ "clang.arc.attachedcall"(i64 0) ] +; CHECK: %[[CALL3:.*]] = invoke i8* @foo() [ "clang.arc.attachedcall"() ] ; CHECK: %[[V2:.*]] = call i8* @llvm.objc.retainAutoreleasedReturnValue(i8* %[[CALL3]]) ; CHECK-NEXT: br @@ -38,7 +38,7 @@ br i1 %b, label %if.then, label %if.end if.then: - %call1 = invoke i8* @foo() [ "clang.arc.attachedcall"(i64 0) ] + %call1 = invoke i8* @foo() [ "clang.arc.attachedcall"(i8* (i8*)* @llvm.objc.retainAutoreleasedReturnValue) ] to label %cleanup unwind label %lpad lpad: @@ -47,7 +47,7 @@ resume { i8*, i32 } undef if.end: - %call3 = invoke i8* @foo() [ "clang.arc.attachedcall"(i64 0) ] + %call3 = invoke i8* @foo() [ "clang.arc.attachedcall"(i8* (i8*)* @llvm.objc.retainAutoreleasedReturnValue) ] to label %cleanup unwind label %lpad cleanup: @@ -57,6 +57,8 @@ declare i8* @foo() declare i32 @__gxx_personality_v0(...) +declare i8* @llvm.objc.retainAutoreleasedReturnValue(i8*) +declare i8* @llvm.objc.unsafeClaimAutoreleasedReturnValue(i8*) !llvm.module.flags = !{!0} diff --git a/llvm/test/Transforms/ObjCARC/rv.ll b/llvm/test/Transforms/ObjCARC/rv.ll --- a/llvm/test/Transforms/ObjCARC/rv.ll +++ b/llvm/test/Transforms/ObjCARC/rv.ll @@ -461,19 +461,19 @@ ; CHECK-NEXT: ret i8* %[[CALL]] define i8* @test31() { - %call = tail call i8* @returner() [ "clang.arc.attachedcall"(i64 0) ] + %call = tail call i8* @returner() [ "clang.arc.attachedcall"(i8* (i8*)* @llvm.objc.retainAutoreleasedReturnValue) ] call void (...) @llvm.objc.clang.arc.noop.use(i8* %call) %1 = call i8* @llvm.objc.autoreleaseReturnValue(i8* %call) ret i8* %1 } ; CHECK-LABEL: define i8* @test32( -; CHECK: %[[CALL:.*]] = call i8* @returner() [ "clang.arc.attachedcall"(i64 0) ] +; CHECK: %[[CALL:.*]] = call i8* @returner() [ "clang.arc.attachedcall"(i8* (i8*)* @llvm.objc.retainAutoreleasedReturnValue) ] ; CHECK: call void (...) @llvm.objc.clang.arc.noop.use(i8* %[[CALL]]) ; CHECK: call i8* @llvm.objc.autoreleaseReturnValue(i8* %[[CALL]]) define i8* @test32() { - %call = call i8* @returner() [ "clang.arc.attachedcall"(i64 0) ] + %call = call i8* @returner() [ "clang.arc.attachedcall"(i8* (i8*)* @llvm.objc.retainAutoreleasedReturnValue) ] call void (...) @llvm.objc.clang.arc.noop.use(i8* %call) %1 = call i8* @llvm.objc.autoreleaseReturnValue(i8* %call) ret i8* %1 diff --git a/llvm/test/Transforms/SCCP/clang-arc-rv.ll b/llvm/test/Transforms/SCCP/clang-arc-rv.ll --- a/llvm/test/Transforms/SCCP/clang-arc-rv.ll +++ b/llvm/test/Transforms/SCCP/clang-arc-rv.ll @@ -16,9 +16,10 @@ ; CHECK call void (...) @llvm.objc.clang.arc.noop.use(i8* %[[R]]) define void @test() { - %r = call i8* @foo() [ "clang.arc.attachedcall"(i64 1) ] + %r = call i8* @foo() [ "clang.arc.attachedcall"(i8* (i8*)* @llvm.objc.unsafeClaimAutoreleasedReturnValue) ] call void (...) @llvm.objc.clang.arc.noop.use(i8* %r) ret void } +declare i8* @llvm.objc.unsafeClaimAutoreleasedReturnValue(i8*) declare void @llvm.objc.clang.arc.noop.use(...) diff --git a/llvm/test/Transforms/TailCallElim/deopt-bundle.ll b/llvm/test/Transforms/TailCallElim/deopt-bundle.ll --- a/llvm/test/Transforms/TailCallElim/deopt-bundle.ll +++ b/llvm/test/Transforms/TailCallElim/deopt-bundle.ll @@ -62,6 +62,8 @@ declare i8* @getObj() define i8* @test_clang_arc_attachedcall() { - %r = call i8* @getObj() [ "clang.arc.attachedcall"(i64 0) ] + %r = call i8* @getObj() [ "clang.arc.attachedcall"(i8* (i8*)* @llvm.objc.retainAutoreleasedReturnValue) ] ret i8* %r } + +declare i8* @llvm.objc.retainAutoreleasedReturnValue(i8*) 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 @@ -66,12 +66,14 @@ define void @f_clang_arc_attachedcall() { ; CHECK: Multiple "clang.arc.attachedcall" operand bundles -; CHECK-NEXT: call %0* @foo0() [ "clang.arc.attachedcall"(i64 0), "clang.arc.attachedcall"(i64 0) ] +; CHECK-NEXT: call %0* @foo0() [ "clang.arc.attachedcall"(i8* (i8*)* @llvm.objc.retainAutoreleasedReturnValue), "clang.arc.attachedcall"(i8* (i8*)* @llvm.objc.retainAutoreleasedReturnValue) ] ; CHECK-NEXT: must call a function returning a pointer -; CHECK-NEXT: call i8 @foo1() [ "clang.arc.attachedcall"(i64 0) ] +; CHECK-NEXT: call i8 @foo1() [ "clang.arc.attachedcall"(i8* (i8*)* @llvm.objc.retainAutoreleasedReturnValue) ] - 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 %0* @foo0() [ "clang.arc.attachedcall"(i8* (i8*)* @llvm.objc.retainAutoreleasedReturnValue) ] + call %0* @foo0() [ "clang.arc.attachedcall"(i8* (i8*)* @llvm.objc.retainAutoreleasedReturnValue), "clang.arc.attachedcall"(i8* (i8*)* @llvm.objc.retainAutoreleasedReturnValue) ] + call i8 @foo1() [ "clang.arc.attachedcall"(i8* (i8*)* @llvm.objc.retainAutoreleasedReturnValue) ] ret void } + +declare i8* @llvm.objc.retainAutoreleasedReturnValue(i8*)