diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -2470,14 +2470,26 @@ ObjC ARC Attached Call Operand Bundles ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -A ``"clang.arc.attachedcall`` operand bundle on a call indicates the call is +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. 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. +function that uses the result of the call. The operand bundle takes either the +pointer to the runtime function (``@objc_retainAutoreleasedReturnValue`` or +``@objc_unsafeClaimAutoreleasedReturnValue``) or no arguments. If the bundle +doesn't take any arguments, only the marker instruction has to be emitted after +the call; the runtime function calls don't have to be emitted since they already +have been emitted. 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. + +.. code-block:: llvm + + ; The marker instruction and a runtime function call are inserted after the call + ; to @foo. + call i8* @foo() [ "clang.arc.attachedcall"(i8* (i8*)* @objc_retainAutoreleasedReturnValue) ] + call i8* @foo() [ "clang.arc.attachedcall"(i8* (i8*)* @objc_unsafeClaimAutoreleasedReturnValue) ] + + ; Only the marker instruction is inserted after the call to @foo. + call i8* @foo() [ "clang.arc.attachedcall"() ] 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,8 @@ #ifndef LLVM_ANALYSIS_OBJCARCUTIL_H #define LLVM_ANALYSIS_OBJCARCUTIL_H +#include "llvm/Analysis/ObjCARCInstKind.h" +#include "llvm/IR/Function.h" #include "llvm/IR/InstrTypes.h" #include "llvm/IR/LLVMContext.h" @@ -24,13 +26,6 @@ return "clang.arc.retainAutoreleasedReturnValueMarker"; } -enum AttachedCallOperandBundle : unsigned { RVOB_Retain, RVOB_Claim }; - -inline AttachedCallOperandBundle -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 @@ -43,14 +38,32 @@ .hasValue(); } -inline bool hasAttachedCallOpBundle(const CallBase *CB, bool IsRetain) { - assert(hasAttachedCallOpBundle(CB) && - "call doesn't have operand bundle clang_arc_attachedcall"); +/// This function returns operand bundle clang_arc_attachedcall's argument, +/// which is the address of the ARC runtime function. +inline Optional getAttachedARCFunction(const CallBase *CB) { auto B = CB->getOperandBundle(LLVMContext::OB_clang_arc_attachedcall); - if (!B.hasValue()) - return false; - return cast(B->Inputs[0])->getZExtValue() == - getAttachedCallOperandBundleEnum(IsRetain); + if (!B.hasValue() || B->Inputs.size() == 0) + return None; + + return cast(B->Inputs[0]); +} + +/// Check whether the function is retainRV/claimRV. +inline bool isRetainOrClaimRV(ARCInstKind Kind) { + return Kind == ARCInstKind::RetainRV || Kind == ARCInstKind::ClaimRV; +} + +/// 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) { + Optional Fn = getAttachedARCFunction(CB); + if (!Fn.hasValue()) + return ARCInstKind::None; + auto FnClass = GetFunctionClass(*Fn); + assert(isRetainOrClaimRV(FnClass) && "unexpected ARC runtime function"); + return FnClass; } } // end namespace objcarc diff --git a/llvm/include/llvm/IR/Function.h b/llvm/include/llvm/IR/Function.h --- a/llvm/include/llvm/IR/Function.h +++ b/llvm/include/llvm/IR/Function.h @@ -846,13 +846,14 @@ /// hasAddressTaken - returns true if there are any uses of this function /// other than direct calls or invokes to it, or blockaddress expressions. /// Optionally passes back an offending user for diagnostic purposes, - /// ignores callback uses, assume like pointer annotation calls, and - /// references in llvm.used and llvm.compiler.used variables. - /// + /// ignores callback uses, assume like pointer annotation calls, references in + /// llvm.used and llvm.compiler.used variables, and operand bundle + /// "clang.arc.attachedcall". bool hasAddressTaken(const User ** = nullptr, bool IgnoreCallbackUses = false, bool IgnoreAssumeLikeCalls = true, - bool IngoreLLVMUsed = false) const; + bool IngoreLLVMUsed = false, + bool IgnoreARCAttachedCall = false) const; /// isDefTriviallyDead - Return true if it is trivially safe to remove /// this function definition from the module (because it isn't externally diff --git a/llvm/include/llvm/IR/InstrTypes.h b/llvm/include/llvm/IR/InstrTypes.h --- a/llvm/include/llvm/IR/InstrTypes.h +++ b/llvm/include/llvm/IR/InstrTypes.h @@ -1928,6 +1928,13 @@ Idx < getBundleOperandsEndIndex(); } + /// Return true if the operand at index \p Idx is a bundle operand that has + /// tag ID \p ID. + bool isOperandBundleOfType(uint32_t ID, unsigned Idx) const { + return isBundleOperand(Idx) && + getOperandBundleForOperand(Idx).getTagID() == ID; + } + /// Returns true if the use is a bundle operand. bool isBundleOperand(const Use *U) const { assert(this == U->getUser() && diff --git a/llvm/lib/IR/Function.cpp b/llvm/lib/IR/Function.cpp --- a/llvm/lib/IR/Function.cpp +++ b/llvm/lib/IR/Function.cpp @@ -1736,8 +1736,8 @@ /// and llvm.compiler.used variables. bool Function::hasAddressTaken(const User **PutOffender, bool IgnoreCallbackUses, - bool IgnoreAssumeLikeCalls, - bool IgnoreLLVMUsed) const { + bool IgnoreAssumeLikeCalls, bool IgnoreLLVMUsed, + bool IgnoreARCAttachedCall) const { for (const Use &U : uses()) { const User *FU = U.getUser(); if (isa(FU)) @@ -1781,6 +1781,11 @@ return true; } if (!Call->isCallee(&U)) { + if (IgnoreARCAttachedCall && + Call->isOperandBundleOfType(LLVMContext::OB_clang_arc_attachedcall, + U.getOperandNo())) + continue; + if (PutOffender) *PutOffender = FU; return true; 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 @@ -569,6 +569,9 @@ /// declarations share the same calling convention. void verifyDeoptimizeCallingConvs(); + void verifyAttachedCallBundle(const CallBase &Call, + const OperandBundleUse &BU); + /// Verify all-or-nothing property of DIFile source attribute within a CU. void verifySourceDebugInfo(const DICompileUnit &U, const DIFile &F); @@ -2521,7 +2524,8 @@ // uses. if (F.isIntrinsic() && F.getParent()->isMaterialized()) { const User *U; - if (F.hasAddressTaken(&U)) + if (F.hasAddressTaken(&U, false, true, false, + /*IgnoreARCAttachedCall=*/true)) Assert(false, "Invalid user of intrinsic instruction!", U); } @@ -3264,17 +3268,10 @@ Assert(!FoundAttachedCallBundle, "Multiple \"clang.arc.attachedcall\" operand bundles", Call); FoundAttachedCallBundle = true; + verifyAttachedCallBundle(Call, BU); } } - if (FoundAttachedCallBundle) - Assert((FTy->getReturnType()->isPointerTy() || - (Call.doesNotReturn() && FTy->getReturnType()->isVoidTy())), - "a call with operand bundle \"clang.arc.attachedcall\" must call a " - "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 // debug-info-bearing function has a debug location attached to it. Failure to // do so causes assertion failures when the inliner sets up inline scope info. @@ -4402,10 +4399,21 @@ } if (Function *F = dyn_cast(I.getOperand(i))) { + // This code checks whether the function is used as the operand of a + // clang_arc_attachedcall operand bundle. + auto IsAttachedCallOperand = [](Function *F, const CallBase *CBI, + int Idx) { + return CBI && CBI->isOperandBundleOfType( + LLVMContext::OB_clang_arc_attachedcall, Idx); + }; + // Check to make sure that the "address of" an intrinsic function is never - // taken. - Assert(!F->isIntrinsic() || - (CBI && &CBI->getCalledOperandUse() == &I.getOperandUse(i)), + // taken. Ignore cases where the address of the intrinsic function is used + // as the argument of operand bundle "clang.arc.attachedcall" as those + // cases are handled in verifyAttachedCallBundle. + 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) || @@ -4419,9 +4427,10 @@ 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", + "statepoint, coro_resume, coro_destroy or clang.arc.attachedcall", &I); Assert(F->getParent() == &M, "Referencing function in another module!", &I, &M, F, F->getParent()); @@ -5642,6 +5651,41 @@ } } +void Verifier::verifyAttachedCallBundle(const CallBase &Call, + const OperandBundleUse &BU) { + FunctionType *FTy = Call.getFunctionType(); + + Assert((FTy->getReturnType()->isPointerTy() || + (Call.doesNotReturn() && FTy->getReturnType()->isVoidTy())), + "a call with operand bundle \"clang.arc.attachedcall\" must call a " + "function returning a pointer or a non-returning function that has a " + "void return type", + Call); + + Assert((BU.Inputs.empty() || + (BU.Inputs.size() == 1 && isa(BU.Inputs.front()))), + "operand bundle \"clang.arc.attachedcall\" can take either no " + "arguments or one function as an argument", + Call); + + if (BU.Inputs.empty()) + return; + + auto *Fn = cast(BU.Inputs.front()); + Intrinsic::ID IID = Fn->getIntrinsicID(); + + if (IID) { + Assert((IID == Intrinsic::objc_retainAutoreleasedReturnValue || + IID == Intrinsic::objc_unsafeClaimAutoreleasedReturnValue), + "invalid function argument", Call); + } else { + StringRef FnName = Fn->getName(); + Assert((FnName == "objc_retainAutoreleasedReturnValue" || + FnName == "objc_unsafeClaimAutoreleasedReturnValue"), + "invalid function argument", Call); + } +} + void Verifier::verifySourceDebugInfo(const DICompileUnit &U, const DIFile &F) { bool HasSource = F.getSource().hasValue(); if (!HasSourceDebugInfo.count(&U)) diff --git a/llvm/lib/Target/X86/X86ExpandPseudo.cpp b/llvm/lib/Target/X86/X86ExpandPseudo.cpp --- a/llvm/lib/Target/X86/X86ExpandPseudo.cpp +++ b/llvm/lib/Target/X86/X86ExpandPseudo.cpp @@ -236,19 +236,10 @@ MBB.getParent()->moveCallSiteInfo(&MI, Marker); // Emit call to ObjC runtime. - unsigned RuntimeCallType = MI.getOperand(0).getImm(); - assert(RuntimeCallType <= 1 && "objc runtime call type must be 0 or 1"); - Module *M = MBB.getParent()->getFunction().getParent(); - auto &Context = M->getContext(); - auto *I8PtrTy = PointerType::get(IntegerType::get(Context, 8), 0); - FunctionCallee Fn = M->getOrInsertFunction( - RuntimeCallType == 0 ? "objc_retainAutoreleasedReturnValue" - : "objc_unsafeClaimAutoreleasedReturnValue", - FunctionType::get(I8PtrTy, {I8PtrTy}, false)); const uint32_t *RegMask = TRI->getCallPreservedMask(*MBB.getParent(), CallingConv::C); BuildMI(MBB, MBBI, MI.getDebugLoc(), TII->get(X86::CALL64pcrel32)) - .addGlobalAddress(cast(Fn.getCallee()), 0, 0) + .addGlobalAddress(MI.getOperand(0).getGlobal(), 0, 0) .addRegMask(RegMask) .addReg(X86::RAX, RegState::Implicit | 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 @@ -4628,14 +4628,12 @@ "tail calls cannot be marked with clang.arc.attachedcall"); assert(Is64Bit && "clang.arc.attachedcall is only supported in 64bit mode"); - // Add target constant to select ObjC runtime call just before the call - // target. RuntimeCallType == 0 selects objc_retainAutoreleasedReturnValue, - // RuntimeCallType == 0 selects objc_unsafeClaimAutoreleasedReturnValue when - // epxanding the pseudo. - unsigned RuntimeCallType = - objcarc::hasAttachedCallOpBundle(CLI.CB, true) ? 0 : 1; - Ops.insert(Ops.begin() + 1, - DAG.getTargetConstant(RuntimeCallType, dl, MVT::i32)); + // Add a target global address for the retainRV/claimRV runtime function + // just before the call target. + Function *ARCFn = *objcarc::getAttachedARCFunction(CLI.CB); + auto PtrVT = getPointerTy(DAG.getDataLayout()); + auto GA = DAG.getTargetGlobalAddress(ARCFn, dl, PtrVT); + Ops.insert(Ops.begin() + 1, GA); Chain = DAG.getNode(X86ISD::CALL_RVMARKER, dl, NodeTys, Ops); } else { Chain = DAG.getNode(X86ISD::CALL, dl, NodeTys, Ops); diff --git a/llvm/lib/Target/X86/X86InstrCompiler.td b/llvm/lib/Target/X86/X86InstrCompiler.td --- a/llvm/lib/Target/X86/X86InstrCompiler.td +++ b/llvm/lib/Target/X86/X86InstrCompiler.td @@ -1215,10 +1215,10 @@ def : Pat<(X86call (i64 texternalsym:$dst)), (CALL64pcrel32 texternalsym:$dst)>; -def : Pat<(X86call_rvmarker (timm:$sel), (i64 texternalsym:$dst)), - (CALL64pcrel32_RVMARKER timm:$sel, texternalsym:$dst)>; -def : Pat<(X86call_rvmarker (timm:$sel), (i64 tglobaladdr:$dst)), - (CALL64pcrel32_RVMARKER timm:$sel, tglobaladdr:$dst)>; +def : Pat<(X86call_rvmarker (i64 tglobaladdr:$rvfunc), (i64 texternalsym:$dst)), + (CALL64pcrel32_RVMARKER tglobaladdr:$rvfunc, texternalsym:$dst)>; +def : Pat<(X86call_rvmarker (i64 tglobaladdr:$rvfunc), (i64 tglobaladdr:$dst)), + (CALL64pcrel32_RVMARKER tglobaladdr:$rvfunc, tglobaladdr:$dst)>; // Tailcall stuff. The TCRETURN instructions execute after the epilog, so they diff --git a/llvm/lib/Target/X86/X86InstrControl.td b/llvm/lib/Target/X86/X86InstrControl.td --- a/llvm/lib/Target/X86/X86InstrControl.td +++ b/llvm/lib/Target/X86/X86InstrControl.td @@ -419,15 +419,15 @@ Uses = [RSP, SSP], SchedRW = [WriteJump] in { def CALL64m_RVMARKER : - PseudoI<(outs), (ins i32imm:$sel, i64mem:$dst), [(X86call_rvmarker timm:$sel, (loadi64 addr:$dst))]>, + PseudoI<(outs), (ins i64imm:$rvfunc, i64mem:$dst), [(X86call_rvmarker tglobaladdr:$rvfunc, (loadi64 addr:$dst))]>, Requires<[In64BitMode]>; def CALL64r_RVMARKER : - PseudoI<(outs), (ins i32imm:$sel, GR64:$dst), [(X86call_rvmarker timm:$sel, GR64:$dst)]>, + PseudoI<(outs), (ins i64imm:$rvfunc, GR64:$dst), [(X86call_rvmarker tglobaladdr:$rvfunc, GR64:$dst)]>, Requires<[In64BitMode]>; def CALL64pcrel32_RVMARKER : - PseudoI<(outs), (ins i32imm:$sel, i64i32imm_brtarget:$dst), []>, + PseudoI<(outs), (ins i64imm:$rvfunc, i64i32imm_brtarget:$dst), []>, Requires<[In64BitMode]>; } 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); + 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 @@ -540,7 +540,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 @@ -2462,7 +2462,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 @@ -1671,10 +1671,11 @@ /// 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(objcarc::isRetainOrClaimRV(RVCallKind) && "unexpected ARC function"); + bool IsRetainRV = RVCallKind == objcarc::ARCInstKind::RetainRV, IsClaimRV = !IsRetainRV; for (auto *RI : Returns) { @@ -1727,9 +1728,7 @@ // 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)}; OperandBundleDef OB("clang.arc.attachedcall", BundleArgs); auto *NewCall = CallBase::addOperandBundle( CI, LLVMContext::OB_clang_arc_attachedcall, OB, CI); @@ -1965,8 +1964,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/CodeGen/X86/expand-call-rvmarker.mir b/llvm/test/CodeGen/X86/expand-call-rvmarker.mir --- a/llvm/test/CodeGen/X86/expand-call-rvmarker.mir +++ b/llvm/test/CodeGen/X86/expand-call-rvmarker.mir @@ -52,7 +52,7 @@ bb.0 (%ir-block.0): frame-setup PUSH64r undef $rax, implicit-def $rsp, implicit $rsp CFI_INSTRUCTION def_cfa_offset 16 - CALL64pcrel32_RVMARKER 0, @fn, csr_64, implicit $rsp, implicit $ssp, implicit-def $rsp, implicit-def $ssp, implicit-def $rax + CALL64pcrel32_RVMARKER @objc_retainAutoreleasedReturnValue, @fn, csr_64, implicit $rsp, implicit $ssp, implicit-def $rsp, implicit-def $ssp, implicit-def $rax $rcx = frame-destroy POP64r implicit-def $rsp, implicit $rsp RET 0, $rax @@ -83,7 +83,7 @@ bb.0 (%ir-block.0): frame-setup PUSH64r undef $rax, implicit-def $rsp, implicit $rsp CFI_INSTRUCTION def_cfa_offset 16 - CALL64pcrel32_RVMARKER 1, @fn, csr_64, implicit $rsp, implicit $ssp, implicit-def $rsp, implicit-def $ssp, implicit-def $rax + CALL64pcrel32_RVMARKER @objc_unsafeClaimAutoreleasedReturnValue, @fn, csr_64, implicit $rsp, implicit $ssp, implicit-def $rsp, implicit-def $ssp, implicit-def $rax $rcx = frame-destroy POP64r implicit-def $rsp, implicit $rsp RET 0, $rax @@ -120,7 +120,7 @@ $rax = MOV64rr $rdi $rdi = MOV64rr killed $rdx $rdx = MOV64rr killed $rax - CALL64pcrel32_RVMARKER 0, @fn, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit $rsi, implicit $rdx, implicit-def $rsp, implicit-def $ssp, implicit-def dead $rax + CALL64pcrel32_RVMARKER @objc_retainAutoreleasedReturnValue, @fn, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit $rsi, implicit $rdx, implicit-def $rsp, implicit-def $ssp, implicit-def dead $rax $rax = frame-destroy POP64r implicit-def $rsp, implicit $rsp RET 0, $rax @@ -150,6 +150,6 @@ bb.0 (%ir-block.0): frame-setup PUSH64r undef $rax, implicit-def $rsp, implicit $rsp CFI_INSTRUCTION def_cfa_offset 16 - CALL64pcrel32_RVMARKER 0, @fn, csr_64, implicit $rsp, implicit $ssp, implicit-def $rsp, implicit-def $ssp, implicit-def dead $rax + CALL64pcrel32_RVMARKER @objc_retainAutoreleasedReturnValue, @fn, csr_64, implicit $rsp, implicit $ssp, implicit-def $rsp, implicit-def $ssp, implicit-def dead $rax $rax = frame-destroy POP64r implicit-def $rsp, implicit $rsp RET 0 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: @@ -55,18 +55,21 @@ ret i8* %retval.0 } +; "clang.arc.attachedcall" is ignored if the return type of the called function is void. ; CHECK-LABEL: define void @test3( -; CHECK: call void @foo2() #[[ATTR1:.*]] [ "clang.arc.attachedcall"(i64 0) ] +; CHECK: call void @foo2() #[[ATTR1:.*]] [ "clang.arc.attachedcall"(i8* (i8*)* @llvm.objc.retainAutoreleasedReturnValue) ] ; CHECK-NEXT: ret void define void @test3() { - call void @foo2() #0 [ "clang.arc.attachedcall"(i64 0) ] + call void @foo2() #0 [ "clang.arc.attachedcall"(i8* (i8*)* @llvm.objc.retainAutoreleasedReturnValue) ] ret void } declare i8* @foo() declare void @foo2() 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/invoke.ll b/llvm/test/Verifier/invoke.ll --- a/llvm/test/Verifier/invoke.ll +++ b/llvm/test/Verifier/invoke.ll @@ -46,7 +46,7 @@ define i8 @f2() personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) { entry: -; CHECK: Cannot invoke an intrinsic other than donothing, patchpoint, statepoint, coro_resume or coro_destroy +; CHECK: Cannot invoke an intrinsic other than donothing, patchpoint, statepoint, coro_resume, coro_destroy or clang.arc.attachedcall invoke void @llvm.trap() to label %cont unwind label %lpad 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 @@ -67,18 +67,40 @@ 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) ] ; CHECK-NEXT: or a non-returning function -; CHECK-NEXT: call void @g() [ "clang.arc.attachedcall"(i64 0) ] +; CHECK-NEXT: call void @g() [ "clang.arc.attachedcall"(i8* (i8*)* @llvm.objc.retainAutoreleasedReturnValue) ] +; CHECK-NEXT: can take either no arguments +; CHECK-NEXT: call %0* @foo0() [ "clang.arc.attachedcall"(i8* (i8*)* null) ] +; CHECK-NEXT: can take either no arguments +; CHECK-NEXT: call %0* @foo0() [ "clang.arc.attachedcall"(i64 0) ] +; CHECK-NEXT: invalid function argument +; CHECK-NEXT: call %0* @foo0() [ "clang.arc.attachedcall"(i8 ()* @foo1) ] +; CHECK-NEXT: invalid function argument +; CHECK-NEXT: call %0* @foo0() [ "clang.arc.attachedcall"(void (i1)* @llvm.assume) ] + call %0* @foo0() [ "clang.arc.attachedcall"(i8* (i8*)* @llvm.objc.retainAutoreleasedReturnValue) ] + call %0* @foo0() [ "clang.arc.attachedcall"(i8* (i8*)* @llvm.objc.unsafeClaimAutoreleasedReturnValue) ] + call %0* @foo0() [ "clang.arc.attachedcall"(i8* (i8*)* @objc_retainAutoreleasedReturnValue) ] + call %0* @foo0() [ "clang.arc.attachedcall"(i8* (i8*)* @objc_unsafeClaimAutoreleasedReturnValue) ] + call %0* @foo0() [ "clang.arc.attachedcall"() ] + 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) ] + call void @noreturn_func() #0 [ "clang.arc.attachedcall"(i8* (i8*)* @llvm.objc.retainAutoreleasedReturnValue) ] + call void @g() [ "clang.arc.attachedcall"(i8* (i8*)* @llvm.objc.retainAutoreleasedReturnValue) ] + call %0* @foo0() [ "clang.arc.attachedcall"(i8* (i8*)* null) ] 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) ] + call %0* @foo0() [ "clang.arc.attachedcall"(i8 ()* @foo1) ] + call %0* @foo0() [ "clang.arc.attachedcall"(void (i1)* @llvm.assume) ] ret void } +declare i8* @llvm.objc.retainAutoreleasedReturnValue(i8*) +declare i8* @llvm.objc.unsafeClaimAutoreleasedReturnValue(i8*) +declare i8* @objc_retainAutoreleasedReturnValue(i8*) +declare i8* @objc_unsafeClaimAutoreleasedReturnValue(i8*) +declare void @llvm.assume(i1) + attributes #0 = { noreturn }