[llvm] 1900503 - [ObjC][ARC] Use operand bundle 'clang.arc.attachedcall' instead of
David Blaikie via llvm-commits
llvm-commits at lists.llvm.org
Fri Mar 5 11:46:05 PST 2021
On Thu, Mar 4, 2021 at 5:43 PM Akira Hatanaka <ahatanak at gmail.com> wrote:
> On Thu, Mar 4, 2021 at 4:20 PM David Blaikie via llvm-commits <
> llvm-commits at lists.llvm.org> wrote:
>
>> Please if at all possible make smaller/incremental patches.
>>
>>
> I think I could have at least split out the front-end part.
>
Could portions of the feature have been implemented incrementally? Not used
by the frontend/live in clang initially, only via llvm unit/lit tests -
incrementally adding functionality until it's fully usable, then switching
the frontend over to it.
In any case, somewhere in all this, Analysis/ObjCARCUtils.h was introduced
>> and included from llvm/IR, violating library layering. (Analysis depends on
>> IR, not the other way around)
>>
>> So I moved this header into IR (please fix otherwise if there's a better
>> answer - but the utilities looked generally applicable to IR) and also
>> fixed a couple of implementation bugs ("LIB" in the header guard macro - we
>> don't usually do that, and the use of non-member "static" in the header -
>> that can lead to ODR violations). In git commit
>> a2a55def354df2cd4de0b1cbd6b2795a07e6905a
>>
>>
> I didn't remember my previous commit had the same problems including the
> layering violation, which were fixed later. Sorry for making the same
> mistake again and thank you for fixing it. I should have just done 'git
> revert'.
>
Ah, yeah, that'll do it - I tend to use `git revert` and then tidy up the
commit message so it doesn't say "Revert of Revert of ... " to something
like "Recommit <original message>" and then in the commit message include a
history of each commit/revert, reason for each revert, what changed with
each subsequent commit, etc.
> As for the 'static inline' functions, you are right. It could lead to
> problems although the code in the patch didn't have ODR violations yet, I
> think?
>
I didn't look too closely, but probably probably? Any include of that
header in another header would be pretty close to a guaranteed ODR
violation - the static function called from a non-static inline function,
and that latter inline function included in two files would be an ODR
violation (because the two definitions of that inline function wouldn't be
identical - they would be referring to different functions (each static
function being distinct))
For instance llvm/lib/Transforms/ObjCARC/ObjCARC.h (oh, also there's a
function in there called "getreturnRVOperand", I guess that should have an
upper case 'R' for Return) has the same error - more non-member static
inline functions. But if those were fixed, `getRVInstMarker` would be an
ODR violation if ObjCARC.h was include in more than one .cpp file.
> I will probably move the header file back to llvm/Analysis and fix the
> code in auto upgrader since auto upgrader was the only reason it had to be
> included in lib/IR.
>
Sure, if that works best, sounds good.
>
>
>> On Thu, Mar 4, 2021 at 11:23 AM Akira Hatanaka via llvm-commits <
>> llvm-commits at lists.llvm.org> wrote:
>>
>>>
>>> Author: Akira Hatanaka
>>> Date: 2021-03-04T11:22:30-08:00
>>> New Revision: 1900503595cbb84a4c6e140a9ba1a2c574c0586d
>>>
>>> URL:
>>> https://github.com/llvm/llvm-project/commit/1900503595cbb84a4c6e140a9ba1a2c574c0586d
>>> DIFF:
>>> https://github.com/llvm/llvm-project/commit/1900503595cbb84a4c6e140a9ba1a2c574c0586d.diff
>>>
>>> LOG: [ObjC][ARC] Use operand bundle 'clang.arc.attachedcall' instead of
>>> explicitly emitting retainRV or claimRV calls in the IR
>>>
>>> This reapplies ed4718eccb12bd42214ca4fb17d196d49561c0c7, which was
>>> reverted
>>> because it was causing a miscompile. The bug that was causing the
>>> miscompile
>>> has been fixed in 75805dce5ff874676f3559c069fcd6737838f5c0.
>>>
>>> Original commit message:
>>>
>>> Background:
>>>
>>> This fixes a longstanding problem where llvm breaks ARC's autorelease
>>> optimization (see the link below) by separating calls from the marker
>>> instructions or retainRV/claimRV calls. The backend changes are in
>>> https://reviews.llvm.org/D92569.
>>>
>>>
>>> https://clang.llvm.org/docs/AutomaticReferenceCounting.html#arc-runtime-objc-autoreleasereturnvalue
>>>
>>> What this patch does to fix the problem:
>>>
>>> - The front-end adds operand bundle "clang.arc.attachedcall" to calls,
>>> which indicates the call is implicitly followed by a marker
>>> instruction and an implicit retainRV/claimRV call that consumes the
>>> call result. In addition, it emits a call to
>>> @llvm.objc.clang.arc.noop.use, which consumes the call result, to
>>> prevent the middle-end passes from changing the return type of the
>>> called function. This is currently done only when the target is arm64
>>> and the optimization level is higher than -O0.
>>>
>>> - ARC optimizer temporarily emits retainRV/claimRV calls after the calls
>>> with the operand bundle in the IR and removes the inserted calls after
>>> processing the function.
>>>
>>> - ARC contract pass emits retainRV/claimRV calls after the call with the
>>> operand bundle. It doesn't remove the operand bundle on the call since
>>> the backend needs it to emit the marker instruction. The retainRV and
>>> claimRV calls are emitted late in the pipeline to prevent optimization
>>> passes from transforming the IR in a way that makes it harder for the
>>> ARC middle-end passes to figure out the def-use relationship between
>>> the call and the retainRV/claimRV calls (which is the cause of
>>> PR31925).
>>>
>>> - The function inliner removes an autoreleaseRV call in the callee if
>>> nothing in the callee prevents it from being paired up with the
>>> retainRV/claimRV call in the caller. It then inserts a release call if
>>> claimRV is attached to the call since autoreleaseRV+claimRV is
>>> equivalent to a release. If it cannot find an autoreleaseRV call, it
>>> tries to transfer the operand bundle to a function call in the callee.
>>> This is important since the ARC optimizer can remove the autoreleaseRV
>>> returning the callee result, which makes it impossible to pair it up
>>> with the retainRV/claimRV call in the caller. If that fails, it simply
>>> emits a retain call in the IR if retainRV is attached to the call and
>>> does nothing if claimRV is attached to it.
>>>
>>> - SCCP refrains from replacing the return value of a call with a
>>> constant value if the call has the operand bundle. This ensures the
>>> call always has at least one user (the call to
>>> @llvm.objc.clang.arc.noop.use).
>>>
>>> - This patch also fixes a bug in replaceUsesOfNonProtoConstant where
>>> multiple operand bundles of the same kind were being added to a call.
>>>
>>> Future work:
>>>
>>> - Use the operand bundle on x86-64.
>>>
>>> - Fix the auto upgrader to convert call+retainRV/claimRV pairs into
>>> calls with the operand bundles.
>>>
>>> rdar://71443534
>>>
>>> Differential Revision: https://reviews.llvm.org/D92808
>>>
>>> Added:
>>> clang/test/CodeGenObjC/arc-rv-attr.m
>>> llvm/include/llvm/Analysis/ObjCARCUtil.h
>>> llvm/test/Transforms/Inline/inline-retainRV-call.ll
>>> llvm/test/Transforms/ObjCARC/contract-rv-attr.ll
>>> llvm/test/Transforms/SCCP/clang-arc-rv.ll
>>>
>>> Modified:
>>> clang/lib/CodeGen/CGObjC.cpp
>>> clang/lib/CodeGen/CodeGenFunction.h
>>> clang/lib/CodeGen/CodeGenModule.cpp
>>> clang/lib/CodeGen/CodeGenModule.h
>>> clang/test/CodeGenObjC/arc-unsafeclaim.m
>>> llvm/docs/LangRef.rst
>>> llvm/include/llvm/IR/InstrTypes.h
>>> llvm/include/llvm/IR/Intrinsics.td
>>> llvm/include/llvm/IR/LLVMContext.h
>>> llvm/lib/Analysis/ObjCARCInstKind.cpp
>>> llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
>>> llvm/lib/IR/AutoUpgrade.cpp
>>> llvm/lib/IR/Instructions.cpp
>>> llvm/lib/IR/LLVMContext.cpp
>>> llvm/lib/IR/Verifier.cpp
>>> llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
>>> llvm/lib/Transforms/ObjCARC/ARCRuntimeEntryPoints.h
>>> llvm/lib/Transforms/ObjCARC/ObjCARC.cpp
>>> llvm/lib/Transforms/ObjCARC/ObjCARC.h
>>> llvm/lib/Transforms/ObjCARC/ObjCARCContract.cpp
>>> llvm/lib/Transforms/ObjCARC/ObjCARCOpts.cpp
>>> llvm/lib/Transforms/ObjCARC/PtrState.cpp
>>> llvm/lib/Transforms/ObjCARC/PtrState.h
>>> llvm/lib/Transforms/Scalar/SCCP.cpp
>>> llvm/lib/Transforms/Scalar/TailRecursionElimination.cpp
>>> llvm/lib/Transforms/Utils/InlineFunction.cpp
>>> llvm/test/Bitcode/operand-bundles-bc-analyzer.ll
>>> llvm/test/CodeGen/AArch64/call-rv-marker.ll
>>> llvm/test/Transforms/DeadArgElim/deadretval.ll
>>> llvm/test/Transforms/ObjCARC/contract-marker-funclet.ll
>>> llvm/test/Transforms/ObjCARC/contract.ll
>>> llvm/test/Transforms/ObjCARC/intrinsic-use.ll
>>> llvm/test/Transforms/ObjCARC/rv.ll
>>> llvm/test/Transforms/TailCallElim/deopt-bundle.ll
>>> llvm/test/Verifier/operand-bundles.ll
>>>
>>> Removed:
>>>
>>>
>>>
>>>
>>> ################################################################################
>>> diff --git a/clang/lib/CodeGen/CGObjC.cpp b/clang/lib/CodeGen/CGObjC.cpp
>>> index 3f930c76fe0a2..663666b6bf8b8 100644
>>> --- a/clang/lib/CodeGen/CGObjC.cpp
>>> +++ b/clang/lib/CodeGen/CGObjC.cpp
>>> @@ -23,6 +23,7 @@
>>> #include "clang/Basic/Diagnostic.h"
>>> #include "clang/CodeGen/CGFunctionInfo.h"
>>> #include "llvm/ADT/STLExtras.h"
>>> +#include "llvm/Analysis/ObjCARCUtil.h"
>>> #include "llvm/BinaryFormat/MachO.h"
>>> #include "llvm/IR/DataLayout.h"
>>> #include "llvm/IR/InlineAsm.h"
>>> @@ -2078,6 +2079,15 @@ void
>>> CodeGenFunction::EmitARCIntrinsicUse(ArrayRef<llvm::Value*> values) {
>>> EmitNounwindRuntimeCall(fn, values);
>>> }
>>>
>>> +/// Emit a call to "clang.arc.noop.use", which consumes the result of a
>>> call
>>> +/// that has operand bundle "clang.arc.attachedcall".
>>> +void CodeGenFunction::EmitARCNoopIntrinsicUse(ArrayRef<llvm::Value *>
>>> values) {
>>> + llvm::Function *&fn = CGM.getObjCEntrypoints().clang_arc_noop_use;
>>> + if (!fn)
>>> + fn = CGM.getIntrinsic(llvm::Intrinsic::objc_clang_arc_noop_use);
>>> + EmitNounwindRuntimeCall(fn, values);
>>> +}
>>> +
>>> static void setARCRuntimeFunctionLinkage(CodeGenModule &CGM,
>>> llvm::Value *RTF) {
>>> if (auto *F = dyn_cast<llvm::Function>(RTF)) {
>>> // If the target runtime doesn't naturally support ARC, emit weak
>>> @@ -2304,10 +2314,11 @@ static void
>>> emitAutoreleasedReturnValueMarker(CodeGenFunction &CGF) {
>>> // with this marker yet, so leave a breadcrumb for the ARC
>>> // optimizer to pick up.
>>> } else {
>>> - const char *markerKey =
>>> "clang.arc.retainAutoreleasedReturnValueMarker";
>>> - if (!CGF.CGM.getModule().getModuleFlag(markerKey)) {
>>> + const char *retainRVMarkerKey =
>>> llvm::objcarc::getRVMarkerModuleFlagStr();
>>> + if (!CGF.CGM.getModule().getModuleFlag(retainRVMarkerKey)) {
>>> auto *str = llvm::MDString::get(CGF.getLLVMContext(), assembly);
>>> - CGF.CGM.getModule().addModuleFlag(llvm::Module::Error,
>>> markerKey, str);
>>> + CGF.CGM.getModule().addModuleFlag(llvm::Module::Error,
>>> + retainRVMarkerKey, str);
>>> }
>>> }
>>> }
>>> @@ -2317,6 +2328,47 @@ static void
>>> emitAutoreleasedReturnValueMarker(CodeGenFunction &CGF) {
>>> CGF.Builder.CreateCall(marker, None,
>>> CGF.getBundlesForFunclet(marker));
>>> }
>>>
>>> +static llvm::Value *emitOptimizedARCReturnCall(llvm::Value *value,
>>> + bool IsRetainRV,
>>> + CodeGenFunction &CGF) {
>>> + emitAutoreleasedReturnValueMarker(CGF);
>>> +
>>> + // Add operand bundle "clang.arc.attachedcall" to the call instead of
>>> emitting
>>> + // retainRV or claimRV calls in the IR. We currently do this only
>>> when the
>>> + // optimization level isn't -O0 since global-isel, which is currently
>>> run at
>>> + // -O0, doesn't know about the operand bundle.
>>> +
>>> + // FIXME: Do this when the target isn't aarch64.
>>> + if (CGF.CGM.getCodeGenOpts().OptimizationLevel > 0 &&
>>> + CGF.CGM.getTarget().getTriple().isAArch64()) {
>>> + llvm::Value *bundleArgs[] = {llvm::ConstantInt::get(
>>> + CGF.Int64Ty,
>>> + llvm::objcarc::getAttachedCallOperandBundleEnum(IsRetainRV))};
>>> + llvm::OperandBundleDef OB("clang.arc.attachedcall", bundleArgs);
>>> + auto *oldCall = cast<llvm::CallBase>(value);
>>> + llvm::CallBase *newCall = llvm::CallBase::addOperandBundle(
>>> + oldCall, llvm::LLVMContext::OB_clang_arc_attachedcall, OB,
>>> oldCall);
>>> + newCall->copyMetadata(*oldCall);
>>> + oldCall->replaceAllUsesWith(newCall);
>>> + oldCall->eraseFromParent();
>>> + CGF.EmitARCNoopIntrinsicUse(newCall);
>>> + return newCall;
>>> + }
>>> +
>>> + bool isNoTail =
>>> +
>>> CGF.CGM.getTargetCodeGenInfo().markARCOptimizedReturnCallsAsNoTail();
>>> + llvm::CallInst::TailCallKind tailKind =
>>> + isNoTail ? llvm::CallInst::TCK_NoTail : llvm::CallInst::TCK_None;
>>> + ObjCEntrypoints &EPs = CGF.CGM.getObjCEntrypoints();
>>> + llvm::Function *&EP = IsRetainRV
>>> + ? EPs.objc_retainAutoreleasedReturnValue
>>> + :
>>> EPs.objc_unsafeClaimAutoreleasedReturnValue;
>>> + llvm::Intrinsic::ID IID =
>>> + IsRetainRV ? llvm::Intrinsic::objc_retainAutoreleasedReturnValue
>>> + :
>>> llvm::Intrinsic::objc_unsafeClaimAutoreleasedReturnValue;
>>> + return emitARCValueOperation(CGF, value, nullptr, EP, IID, tailKind);
>>> +}
>>> +
>>> /// Retain the given object which is the result of a function call.
>>> /// call i8* \@objc_retainAutoreleasedReturnValue(i8* %value)
>>> ///
>>> @@ -2324,15 +2376,7 @@ static void
>>> emitAutoreleasedReturnValueMarker(CodeGenFunction &CGF) {
>>> /// call with completely
>>> diff erent semantics.
>>> llvm::Value *
>>> CodeGenFunction::EmitARCRetainAutoreleasedReturnValue(llvm::Value
>>> *value) {
>>> - emitAutoreleasedReturnValueMarker(*this);
>>> - llvm::CallInst::TailCallKind tailKind =
>>> - CGM.getTargetCodeGenInfo().markARCOptimizedReturnCallsAsNoTail()
>>> - ? llvm::CallInst::TCK_NoTail
>>> - : llvm::CallInst::TCK_None;
>>> - return emitARCValueOperation(
>>> - *this, value, nullptr,
>>> - CGM.getObjCEntrypoints().objc_retainAutoreleasedReturnValue,
>>> - llvm::Intrinsic::objc_retainAutoreleasedReturnValue, tailKind);
>>> + return emitOptimizedARCReturnCall(value, true, *this);
>>> }
>>>
>>> /// Claim a possibly-autoreleased return value at +0. This is only
>>> @@ -2344,15 +2388,7 @@
>>> CodeGenFunction::EmitARCRetainAutoreleasedReturnValue(llvm::Value *value) {
>>> /// call i8* \@objc_unsafeClaimAutoreleasedReturnValue(i8* %value)
>>> llvm::Value *
>>> CodeGenFunction::EmitARCUnsafeClaimAutoreleasedReturnValue(llvm::Value
>>> *value) {
>>> - emitAutoreleasedReturnValueMarker(*this);
>>> - llvm::CallInst::TailCallKind tailKind =
>>> - CGM.getTargetCodeGenInfo().markARCOptimizedReturnCallsAsNoTail()
>>> - ? llvm::CallInst::TCK_NoTail
>>> - : llvm::CallInst::TCK_None;
>>> - return emitARCValueOperation(
>>> - *this, value, nullptr,
>>> - CGM.getObjCEntrypoints().objc_unsafeClaimAutoreleasedReturnValue,
>>> - llvm::Intrinsic::objc_unsafeClaimAutoreleasedReturnValue,
>>> tailKind);
>>> + return emitOptimizedARCReturnCall(value, false, *this);
>>> }
>>>
>>> /// Release the given object.
>>>
>>> diff --git a/clang/lib/CodeGen/CodeGenFunction.h
>>> b/clang/lib/CodeGen/CodeGenFunction.h
>>> index 279ee857ecd22..8ef0de018a98b 100644
>>> --- a/clang/lib/CodeGen/CodeGenFunction.h
>>> +++ b/clang/lib/CodeGen/CodeGenFunction.h
>>> @@ -4216,6 +4216,8 @@ class CodeGenFunction : public CodeGenTypeCache {
>>>
>>> void EmitARCIntrinsicUse(ArrayRef<llvm::Value*> values);
>>>
>>> + void EmitARCNoopIntrinsicUse(ArrayRef<llvm::Value *> values);
>>> +
>>> static Destroyer destroyARCStrongImprecise;
>>> static Destroyer destroyARCStrongPrecise;
>>> static Destroyer destroyARCWeak;
>>>
>>> diff --git a/clang/lib/CodeGen/CodeGenModule.cpp
>>> b/clang/lib/CodeGen/CodeGenModule.cpp
>>> index b23d995683bf7..5623bd7542ec5 100644
>>> --- a/clang/lib/CodeGen/CodeGenModule.cpp
>>> +++ b/clang/lib/CodeGen/CodeGenModule.cpp
>>> @@ -4573,7 +4573,6 @@ static void
>>> replaceUsesOfNonProtoConstant(llvm::Constant *old,
>>>
>>> llvm::Type *newRetTy = newFn->getReturnType();
>>> SmallVector<llvm::Value*, 4> newArgs;
>>> - SmallVector<llvm::OperandBundleDef, 1> newBundles;
>>>
>>> for (llvm::Value::use_iterator ui = old->use_begin(), ue =
>>> old->use_end();
>>> ui != ue; ) {
>>> @@ -4630,6 +4629,7 @@ static void
>>> replaceUsesOfNonProtoConstant(llvm::Constant *old,
>>> newArgs.append(callSite->arg_begin(), callSite->arg_begin() +
>>> argNo);
>>>
>>> // Copy over any operand bundles.
>>> + SmallVector<llvm::OperandBundleDef, 1> newBundles;
>>> callSite->getOperandBundlesAsDefs(newBundles);
>>>
>>> llvm::CallBase *newCall;
>>>
>>> diff --git a/clang/lib/CodeGen/CodeGenModule.h
>>> b/clang/lib/CodeGen/CodeGenModule.h
>>> index 409ebd3df07fe..d495a169b609b 100644
>>> --- a/clang/lib/CodeGen/CodeGenModule.h
>>> +++ b/clang/lib/CodeGen/CodeGenModule.h
>>> @@ -210,6 +210,9 @@ struct ObjCEntrypoints {
>>>
>>> /// void clang.arc.use(...);
>>> llvm::Function *clang_arc_use;
>>> +
>>> + /// void clang.arc.noop.use(...);
>>> + llvm::Function *clang_arc_noop_use;
>>> };
>>>
>>> /// This class records statistics on instrumentation based profiling.
>>>
>>> diff --git a/clang/test/CodeGenObjC/arc-rv-attr.m
>>> b/clang/test/CodeGenObjC/arc-rv-attr.m
>>> new file mode 100644
>>> index 0000000000000..3b769de1443c7
>>> --- /dev/null
>>> +++ b/clang/test/CodeGenObjC/arc-rv-attr.m
>>> @@ -0,0 +1,177 @@
>>> +// RUN: %clang_cc1 -triple arm64-apple-ios9 -fobjc-runtime=ios-9.0
>>> -fobjc-arc -O -disable-llvm-passes -emit-llvm -o - %s | FileCheck %s
>>> -check-prefix=CHECK
>>> +
>>> + at class A;
>>> +
>>> +A *makeA(void);
>>> +
>>> +void test_assign() {
>>> + __unsafe_unretained id x;
>>> + x = makeA();
>>> +}
>>> +// CHECK-LABEL: define{{.*}} void @test_assign()
>>> +// CHECK: [[X:%.*]] = alloca i8*
>>> +// CHECK: [[T0:%.*]] = call [[A:.*]]* @makeA() [
>>> "clang.arc.attachedcall"(i64 1) ]
>>> +// CHECK-NEXT: call void (...) @llvm.objc.clang.arc.noop.use({{.*}}
>>> [[T0]])
>>> +// CHECK-NEXT: [[T1:%.*]] = bitcast [[A]]* [[T0]] to i8*
>>> +// CHECK-NEXT: store i8* [[T1]], i8** [[X]]
>>> +// CHECK-NEXT: bitcast
>>> +// CHECK-NEXT: lifetime.end
>>> +// CHECK-NEXT: ret void
>>> +
>>> +void test_assign_assign() {
>>> + __unsafe_unretained id x, y;
>>> + x = y = makeA();
>>> +}
>>> +// CHECK-LABEL: define{{.*}} void @test_assign_assign()
>>> +// CHECK: [[X:%.*]] = alloca i8*
>>> +// CHECK: [[Y:%.*]] = alloca i8*
>>> +// CHECK: [[T0:%.*]] = call [[A]]* @makeA() [
>>> "clang.arc.attachedcall"(i64 1) ]
>>> +// CHECK-NEXT: call void (...) @llvm.objc.clang.arc.noop.use({{.*}}
>>> [[T0]])
>>> +// CHECK-NEXT: [[T1:%.*]] = bitcast [[A]]* [[T0]] to i8*
>>> +// CHECK-NEXT: store i8* [[T1]], i8** [[Y]]
>>> +// CHECK-NEXT: store i8* [[T1]], i8** [[X]]
>>> +// CHECK-NEXT: bitcast
>>> +// CHECK-NEXT: lifetime.end
>>> +// CHECK-NEXT: bitcast
>>> +// CHECK-NEXT: lifetime.end
>>> +// CHECK-NEXT: ret void
>>> +
>>> +void test_strong_assign_assign() {
>>> + __strong id x;
>>> + __unsafe_unretained id y;
>>> + x = y = makeA();
>>> +}
>>> +// CHECK-LABEL: define{{.*}} void @test_strong_assign_assign()
>>> +// CHECK: [[X:%.*]] = alloca i8*
>>> +// CHECK: [[Y:%.*]] = alloca i8*
>>> +// CHECK: [[T0:%.*]] = call [[A]]* @makeA() [
>>> "clang.arc.attachedcall"(i64 0) ]
>>> +// CHECK-NEXT: call void (...) @llvm.objc.clang.arc.noop.use({{.*}}
>>> [[T0]])
>>> +// CHECK-NEXT: [[T1:%.*]] = bitcast [[A]]* [[T0]] to i8*
>>> +// CHECK-NEXT: store i8* [[T1]], i8** [[Y]]
>>> +// CHECK-NEXT: [[OLD:%.*]] = load i8*, i8** [[X]]
>>> +// CHECK-NEXT: store i8* [[T1]], i8** [[X]]
>>> +// CHECK-NEXT: call void @llvm.objc.release(i8* [[OLD]]
>>> +// CHECK-NEXT: bitcast
>>> +// CHECK-NEXT: lifetime.end
>>> +// CHECK-NEXT: [[T0:%.*]] = load i8*, i8** [[X]]
>>> +// CHECK-NEXT: call void @llvm.objc.release(i8* [[T0]])
>>> +// CHECK-NEXT: bitcast
>>> +// CHECK-NEXT: lifetime.end
>>> +// CHECK-NEXT: ret void
>>> +
>>> +void test_assign_strong_assign() {
>>> + __unsafe_unretained id x;
>>> + __strong id y;
>>> + x = y = makeA();
>>> +}
>>> +// CHECK-LABEL: define{{.*}} void @test_assign_strong_assign()
>>> +// CHECK: [[X:%.*]] = alloca i8*
>>> +// CHECK: [[Y:%.*]] = alloca i8*
>>> +// CHECK: [[T0:%.*]] = call [[A]]* @makeA() [
>>> "clang.arc.attachedcall"(i64 0) ]
>>> +// CHECK-NEXT: call void (...) @llvm.objc.clang.arc.noop.use({{.*}}
>>> [[T0]])
>>> +// CHECK-NEXT: [[T1:%.*]] = bitcast [[A]]* [[T0]] to i8*
>>> +// CHECK-NEXT: [[OLD:%.*]] = load i8*, i8** [[Y]]
>>> +// CHECK-NEXT: store i8* [[T1]], i8** [[Y]]
>>> +// CHECK-NEXT: call void @llvm.objc.release(i8* [[OLD]]
>>> +// CHECK-NEXT: store i8* [[T1]], i8** [[X]]
>>> +// CHECK-NEXT: [[T0:%.*]] = load i8*, i8** [[Y]]
>>> +// CHECK-NEXT: call void @llvm.objc.release(i8* [[T0]])
>>> +// CHECK-NEXT: bitcast
>>> +// CHECK-NEXT: lifetime.end
>>> +// CHECK-NEXT: bitcast
>>> +// CHECK-NEXT: lifetime.end
>>> +// CHECK-NEXT: ret void
>>> +
>>> +void test_init() {
>>> + __unsafe_unretained id x = makeA();
>>> +}
>>> +// CHECK-LABEL: define{{.*}} void @test_init()
>>> +// CHECK: [[X:%.*]] = alloca i8*
>>> +// CHECK: [[T0:%.*]] = call [[A]]* @makeA() [
>>> "clang.arc.attachedcall"(i64 1) ]
>>> +// CHECK-NEXT: call void (...) @llvm.objc.clang.arc.noop.use({{.*}}
>>> [[T0]])
>>> +// CHECK-NEXT: [[T1:%.*]] = bitcast [[A]]* [[T0]] to i8*
>>> +// CHECK-NEXT: store i8* [[T1]], i8** [[X]]
>>> +// CHECK-NEXT: bitcast
>>> +// CHECK-NEXT: lifetime.end
>>> +// CHECK-NEXT: ret void
>>> +
>>> +void test_init_assignment() {
>>> + __unsafe_unretained id x;
>>> + __unsafe_unretained id y = x = makeA();
>>> +}
>>> +// CHECK-LABEL: define{{.*}} void @test_init_assignment()
>>> +// CHECK: [[X:%.*]] = alloca i8*
>>> +// CHECK: [[Y:%.*]] = alloca i8*
>>> +// CHECK: [[T0:%.*]] = call [[A]]* @makeA() [
>>> "clang.arc.attachedcall"(i64 1) ]
>>> +// CHECK-NEXT: call void (...) @llvm.objc.clang.arc.noop.use({{.*}}
>>> [[T0]])
>>> +// CHECK-NEXT: [[T1:%.*]] = bitcast [[A]]* [[T0]] to i8*
>>> +// CHECK-NEXT: store i8* [[T1]], i8** [[X]]
>>> +// CHECK-NEXT: store i8* [[T1]], i8** [[Y]]
>>> +// CHECK-NEXT: bitcast
>>> +// CHECK-NEXT: lifetime.end
>>> +// CHECK-NEXT: bitcast
>>> +// CHECK-NEXT: lifetime.end
>>> +// CHECK-NEXT: ret void
>>> +
>>> +void test_strong_init_assignment() {
>>> + __unsafe_unretained id x;
>>> + __strong id y = x = makeA();
>>> +}
>>> +// CHECK-LABEL: define{{.*}} void @test_strong_init_assignment()
>>> +// CHECK: [[X:%.*]] = alloca i8*
>>> +// CHECK: [[Y:%.*]] = alloca i8*
>>> +// CHECK: [[T0:%.*]] = call [[A]]* @makeA() [
>>> "clang.arc.attachedcall"(i64 0) ]
>>> +// CHECK-NEXT: call void (...) @llvm.objc.clang.arc.noop.use({{.*}}
>>> [[T0]])
>>> +// CHECK-NEXT: [[T1:%.*]] = bitcast [[A]]* [[T0]] to i8*
>>> +// CHECK-NEXT: store i8* [[T1]], i8** [[X]]
>>> +// CHECK-NEXT: store i8* [[T1]], i8** [[Y]]
>>> +// CHECK-NEXT: [[T0:%.*]] = load i8*, i8** [[Y]]
>>> +// CHECK-NEXT: call void @llvm.objc.release(i8* [[T0]])
>>> +// CHECK-NEXT: bitcast
>>> +// CHECK-NEXT: lifetime.end
>>> +// CHECK-NEXT: bitcast
>>> +// CHECK-NEXT: lifetime.end
>>> +// CHECK-NEXT: ret void
>>> +
>>> +void test_init_strong_assignment() {
>>> + __strong id x;
>>> + __unsafe_unretained id y = x = makeA();
>>> +}
>>> +// CHECK-LABEL: define{{.*}} void @test_init_strong_assignment()
>>> +// CHECK: [[X:%.*]] = alloca i8*
>>> +// CHECK: [[Y:%.*]] = alloca i8*
>>> +// CHECK: [[T0:%.*]] = call [[A]]* @makeA() [
>>> "clang.arc.attachedcall"(i64 0) ]
>>> +// CHECK-NEXT: call void (...) @llvm.objc.clang.arc.noop.use({{.*}}
>>> [[T0]])
>>> +// CHECK-NEXT: [[T1:%.*]] = bitcast [[A]]* [[T0]] to i8*
>>> +// CHECK-NEXT: [[OLD:%.*]] = load i8*, i8** [[X]]
>>> +// CHECK-NEXT: store i8* [[T1]], i8** [[X]]
>>> +// CHECK-NEXT: call void @llvm.objc.release(i8* [[OLD]])
>>> +// CHECK-NEXT: store i8* [[T1]], i8** [[Y]]
>>> +// CHECK-NEXT: bitcast
>>> +// CHECK-NEXT: lifetime.end
>>> +// CHECK-NEXT: [[T0:%.*]] = load i8*, i8** [[X]]
>>> +// CHECK-NEXT: call void @llvm.objc.release(i8* [[T0]])
>>> +// CHECK-NEXT: bitcast
>>> +// CHECK-NEXT: lifetime.end
>>> +// CHECK-NEXT: ret void
>>> +
>>> +void test_ignored() {
>>> + makeA();
>>> +}
>>> +// CHECK-LABEL: define{{.*}} void @test_ignored()
>>> +// CHECK: [[T0:%.*]] = call [[A]]* @makeA() [
>>> "clang.arc.attachedcall"(i64 1) ]
>>> +// CHECK-NEXT: call void (...) @llvm.objc.clang.arc.noop.use({{.*}}
>>> [[T0]])
>>> +// CHECK-NEXT: ret void
>>> +
>>> +void test_cast_to_void() {
>>> + (void) makeA();
>>> +}
>>> +// CHECK-LABEL: define{{.*}} void @test_cast_to_void()
>>> +// CHECK: [[T0:%.*]] = call [[A]]* @makeA() [
>>> "clang.arc.attachedcall"(i64 1) ]
>>> +// CHECK-NEXT: call void (...) @llvm.objc.clang.arc.noop.use({{.*}}
>>> [[T0]])
>>> +// CHECK-NEXT: ret void
>>> +
>>> +// This is always at the end of the module.
>>> +
>>> +// CHECK-OPTIMIZED: !llvm.module.flags = !{!0,
>>> +// CHECK-OPTIMIZED: !0 = !{i32 1,
>>> !"clang.arc.retainAutoreleasedReturnValueMarker", !"mov{{.*}}marker for
>>> objc_retainAutoreleaseReturnValue"}
>>>
>>> diff --git a/clang/test/CodeGenObjC/arc-unsafeclaim.m
>>> b/clang/test/CodeGenObjC/arc-unsafeclaim.m
>>> index 8f95d1edc96c5..08ff8eca8da51 100644
>>> --- a/clang/test/CodeGenObjC/arc-unsafeclaim.m
>>> +++ b/clang/test/CodeGenObjC/arc-unsafeclaim.m
>>> @@ -4,11 +4,10 @@
>>> // Make sure it works on x86-32.
>>> // RUN: %clang_cc1 -triple i386-apple-darwin11
>>> -fobjc-runtime=macosx-fragile-10.11 -fobjc-arc -emit-llvm -o - %s |
>>> FileCheck %s -check-prefix=CHECK -check-prefix=CHECK-UNOPTIMIZED
>>> -check-prefix=CHECK-MARKED -check-prefix=CALL
>>>
>>> -// Make sure it works on ARM.
>>> +// Make sure it works on ARM64.
>>> // RUN: %clang_cc1 -triple arm64-apple-ios9 -fobjc-runtime=ios-9.0
>>> -fobjc-arc -emit-llvm -o - %s | FileCheck %s -check-prefix=CHECK
>>> -check-prefix=CHECK-UNOPTIMIZED -check-prefix=CHECK-MARKED
>>> -check-prefix=CALL
>>> -// RUN: %clang_cc1 -triple arm64-apple-ios9 -fobjc-runtime=ios-9.0
>>> -fobjc-arc -O -disable-llvm-passes -emit-llvm -o - %s | FileCheck %s
>>> -check-prefix=CHECK -check-prefix=CHECK-OPTIMIZED -check-prefix=CALL
>>>
>>> -// Make sure it works on ARM64.
>>> +// Make sure it works on ARM.
>>> // RUN: %clang_cc1 -triple armv7-apple-ios9 -fobjc-runtime=ios-9.0
>>> -fobjc-arc -emit-llvm -o - %s | FileCheck %s -check-prefix=CHECK
>>> -check-prefix=CHECK-UNOPTIMIZED -check-prefix=CHECK-MARKED
>>> -check-prefix=CALL
>>> // RUN: %clang_cc1 -triple armv7-apple-ios9 -fobjc-runtime=ios-9.0
>>> -fobjc-arc -O -disable-llvm-passes -emit-llvm -o - %s | FileCheck %s
>>> -check-prefix=CHECK -check-prefix=CHECK-OPTIMIZED -check-prefix=CALL
>>>
>>>
>>> diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
>>> index fef7a2af8d15f..343aeaec78bb8 100644
>>> --- a/llvm/docs/LangRef.rst
>>> +++ b/llvm/docs/LangRef.rst
>>> @@ -2328,6 +2328,19 @@ When lowered, any relocated value will be
>>> recorded in the corresponding
>>> :ref:`stackmap entry <statepoint-stackmap-format>`. See the intrinsic
>>> description
>>> for further details.
>>>
>>> +ObjC ARC Attached Call Operand Bundles
>>> +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>>> +
>>> +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.
>>> +
>>> +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.
>>> +
>>> .. _moduleasm:
>>>
>>> Module-Level Inline Assembly
>>>
>>> diff --git a/llvm/include/llvm/Analysis/ObjCARCUtil.h
>>> b/llvm/include/llvm/Analysis/ObjCARCUtil.h
>>> new file mode 100644
>>> index 0000000000000..9345c892ff399
>>> --- /dev/null
>>> +++ b/llvm/include/llvm/Analysis/ObjCARCUtil.h
>>> @@ -0,0 +1,50 @@
>>> +//===- ObjCARCUtil.h - ObjC ARC Utility Functions ---------------*- C++
>>> -*-===//
>>> +//
>>> +// Part of the LLVM Project, under the Apache License v2.0 with LLVM
>>> Exceptions.
>>> +// See https://llvm.org/LICENSE.txt for license information.
>>> +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
>>> +//
>>>
>>> +//===----------------------------------------------------------------------===//
>>> +/// \file
>>> +/// This file defines ARC utility functions which are used by various
>>> parts of
>>> +/// the compiler.
>>> +///
>>>
>>> +//===----------------------------------------------------------------------===//
>>> +
>>> +#ifndef LLVM_LIB_ANALYSIS_OBJCARCUTIL_H
>>> +#define LLVM_LIB_ANALYSIS_OBJCARCUTIL_H
>>> +
>>> +#include "llvm/IR/InstrTypes.h"
>>> +#include "llvm/IR/LLVMContext.h"
>>> +
>>> +namespace llvm {
>>> +namespace objcarc {
>>> +
>>> +static inline const char *getRVMarkerModuleFlagStr() {
>>> + return "clang.arc.retainAutoreleasedReturnValueMarker";
>>> +}
>>> +
>>> +enum AttachedCallOperandBundle : unsigned { RVOB_Retain, RVOB_Claim };
>>> +
>>> +static AttachedCallOperandBundle
>>> +getAttachedCallOperandBundleEnum(bool IsRetain) {
>>> + return IsRetain ? RVOB_Retain : RVOB_Claim;
>>> +}
>>> +
>>> +static inline bool hasAttachedCallOpBundle(const CallBase *CB, bool
>>> IsRetain) {
>>> + auto B = CB->getOperandBundle(LLVMContext::OB_clang_arc_attachedcall);
>>> + if (!B.hasValue())
>>> + return false;
>>> + return cast<ConstantInt>(B->Inputs[0])->getZExtValue() ==
>>> + getAttachedCallOperandBundleEnum(IsRetain);
>>> +}
>>> +
>>> +static inline bool hasAttachedCallOpBundle(const CallBase *CB) {
>>> + return CB->getOperandBundle(LLVMContext::OB_clang_arc_attachedcall)
>>> + .hasValue();
>>> +}
>>> +
>>> +} // end namespace objcarc
>>> +} // end namespace llvm
>>> +
>>> +#endif
>>>
>>> diff --git a/llvm/include/llvm/IR/InstrTypes.h
>>> b/llvm/include/llvm/IR/InstrTypes.h
>>> index 906264b7d91d9..5c1d2bdd296ea 100644
>>> --- a/llvm/include/llvm/IR/InstrTypes.h
>>> +++ b/llvm/include/llvm/IR/InstrTypes.h
>>> @@ -1223,6 +1223,15 @@ class CallBase : public Instruction {
>>> OperandBundleDef Bundle,
>>> Instruction *InsertPt = nullptr);
>>>
>>> + /// Create a clone of \p CB with operand bundle \p OB added.
>>> + static CallBase *addOperandBundle(CallBase *CB, uint32_t ID,
>>> + OperandBundleDef OB,
>>> + Instruction *InsertPt = nullptr);
>>> +
>>> + /// Create a clone of \p CB with operand bundle \p ID removed.
>>> + static CallBase *removeOperandBundle(CallBase *CB, uint32_t ID,
>>> + Instruction *InsertPt = nullptr);
>>> +
>>> static bool classof(const Instruction *I) {
>>> return I->getOpcode() == Instruction::Call ||
>>> I->getOpcode() == Instruction::Invoke ||
>>>
>>> diff --git a/llvm/include/llvm/IR/Intrinsics.td
>>> b/llvm/include/llvm/IR/Intrinsics.td
>>> index 385fdfa34b1b9..2ada767fa0a26 100644
>>> --- a/llvm/include/llvm/IR/Intrinsics.td
>>> +++ b/llvm/include/llvm/IR/Intrinsics.td
>>> @@ -450,6 +450,9 @@ def int_objc_storeWeak :
>>> Intrinsic<[llvm_ptr_ty],
>>> llvm_ptr_ty]>;
>>> def int_objc_clang_arc_use : Intrinsic<[],
>>>
>>> [llvm_vararg_ty]>;
>>> +def int_objc_clang_arc_noop_use : DefaultAttrsIntrinsic<[],
>>> +
>>> [llvm_vararg_ty],
>>> +
>>> [IntrInaccessibleMemOnly]>;
>>> def int_objc_unsafeClaimAutoreleasedReturnValue :
>>> Intrinsic<[llvm_ptr_ty],
>>>
>>> [llvm_ptr_ty]>;
>>> def int_objc_retainedObject : Intrinsic<[llvm_ptr_ty],
>>>
>>> diff --git a/llvm/include/llvm/IR/LLVMContext.h
>>> b/llvm/include/llvm/IR/LLVMContext.h
>>> index 16eb5c69d3b78..3bd889485dd1d 100644
>>> --- a/llvm/include/llvm/IR/LLVMContext.h
>>> +++ b/llvm/include/llvm/IR/LLVMContext.h
>>> @@ -87,12 +87,13 @@ class LLVMContext {
>>> /// operand bundle tags without comparing strings. Keep this in sync
>>> with
>>> /// LLVMContext::LLVMContext().
>>> enum : unsigned {
>>> - OB_deopt = 0, // "deopt"
>>> - OB_funclet = 1, // "funclet"
>>> - OB_gc_transition = 2, // "gc-transition"
>>> - OB_cfguardtarget = 3, // "cfguardtarget"
>>> - OB_preallocated = 4, // "preallocated"
>>> - OB_gc_live = 5, // "gc-live"
>>> + OB_deopt = 0, // "deopt"
>>> + OB_funclet = 1, // "funclet"
>>> + OB_gc_transition = 2, // "gc-transition"
>>> + OB_cfguardtarget = 3, // "cfguardtarget"
>>> + OB_preallocated = 4, // "preallocated"
>>> + OB_gc_live = 5, // "gc-live"
>>> + OB_clang_arc_attachedcall = 6, // "clang.arc.attachedcall"
>>> };
>>>
>>> /// getMDKindID - Return a unique non-zero ID for the specified
>>> metadata kind.
>>>
>>> diff --git a/llvm/lib/Analysis/ObjCARCInstKind.cpp
>>> b/llvm/lib/Analysis/ObjCARCInstKind.cpp
>>> index 9519078042271..704d15f3280dc 100644
>>> --- a/llvm/lib/Analysis/ObjCARCInstKind.cpp
>>> +++ b/llvm/lib/Analysis/ObjCARCInstKind.cpp
>>> @@ -140,6 +140,7 @@ ARCInstKind llvm::objcarc::GetFunctionClass(const
>>> Function *F) {
>>> return ARCInstKind::User;
>>> case Intrinsic::objc_sync_exit:
>>> return ARCInstKind::User;
>>> + case Intrinsic::objc_clang_arc_noop_use:
>>> case Intrinsic::objc_arc_annotation_topdown_bbstart:
>>> case Intrinsic::objc_arc_annotation_topdown_bbend:
>>> case Intrinsic::objc_arc_annotation_bottomup_bbstart:
>>>
>>> diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
>>> b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
>>> index 3e574dd8fb72d..39730d03502b7 100644
>>> --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
>>> +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
>>> @@ -2784,11 +2784,11 @@ void SelectionDAGBuilder::visitInvoke(const
>>> InvokeInst &I) {
>>>
>>> // Deopt bundles are lowered in LowerCallSiteWithDeoptBundle, and we
>>> don't
>>> // have to do anything here to lower funclet bundles.
>>> - assert(!I.hasOperandBundlesOtherThan({LLVMContext::OB_deopt,
>>> - LLVMContext::OB_gc_transition,
>>> - LLVMContext::OB_gc_live,
>>> - LLVMContext::OB_funclet,
>>> - LLVMContext::OB_cfguardtarget})
>>> &&
>>> + assert(!I.hasOperandBundlesOtherThan(
>>> + {LLVMContext::OB_deopt, LLVMContext::OB_gc_transition,
>>> + LLVMContext::OB_gc_live, LLVMContext::OB_funclet,
>>> + LLVMContext::OB_cfguardtarget,
>>> + LLVMContext::OB_clang_arc_attachedcall}) &&
>>> "Cannot lower invokes with arbitrary operand bundles yet!");
>>>
>>> const Value *Callee(I.getCalledOperand());
>>> @@ -7895,7 +7895,8 @@ void SelectionDAGBuilder::visitCall(const CallInst
>>> &I) {
>>> // CFGuardTarget bundles are lowered in LowerCallTo.
>>> assert(!I.hasOperandBundlesOtherThan(
>>> {LLVMContext::OB_deopt, LLVMContext::OB_funclet,
>>> - LLVMContext::OB_cfguardtarget,
>>> LLVMContext::OB_preallocated}) &&
>>> + LLVMContext::OB_cfguardtarget,
>>> LLVMContext::OB_preallocated,
>>> + LLVMContext::OB_clang_arc_attachedcall}) &&
>>> "Cannot lower calls with arbitrary operand bundles!");
>>>
>>> SDValue Callee = getValue(I.getCalledOperand());
>>>
>>> diff --git a/llvm/lib/IR/AutoUpgrade.cpp b/llvm/lib/IR/AutoUpgrade.cpp
>>> index 1250b5d2a906f..8ca091d436c88 100644
>>> --- a/llvm/lib/IR/AutoUpgrade.cpp
>>> +++ b/llvm/lib/IR/AutoUpgrade.cpp
>>> @@ -14,14 +14,15 @@
>>>
>>> #include "llvm/IR/AutoUpgrade.h"
>>> #include "llvm/ADT/StringSwitch.h"
>>> +#include "llvm/Analysis/ObjCARCUtil.h"
>>> #include "llvm/IR/Constants.h"
>>> #include "llvm/IR/DIBuilder.h"
>>> #include "llvm/IR/DebugInfo.h"
>>> #include "llvm/IR/DiagnosticInfo.h"
>>> #include "llvm/IR/Function.h"
>>> #include "llvm/IR/IRBuilder.h"
>>> -#include "llvm/IR/Instruction.h"
>>> #include "llvm/IR/InstVisitor.h"
>>> +#include "llvm/IR/Instruction.h"
>>> #include "llvm/IR/IntrinsicInst.h"
>>> #include "llvm/IR/Intrinsics.h"
>>> #include "llvm/IR/IntrinsicsAArch64.h"
>>> @@ -4038,7 +4039,7 @@ bool llvm::UpgradeDebugInfo(Module &M) {
>>> /// returns true if module is modified.
>>> static bool UpgradeRetainReleaseMarker(Module &M) {
>>> bool Changed = false;
>>> - const char *MarkerKey =
>>> "clang.arc.retainAutoreleasedReturnValueMarker";
>>> + const char *MarkerKey = objcarc::getRVMarkerModuleFlagStr();
>>> NamedMDNode *ModRetainReleaseMarker = M.getNamedMetadata(MarkerKey);
>>> if (ModRetainReleaseMarker) {
>>> MDNode *Op = ModRetainReleaseMarker->getOperand(0);
>>>
>>> diff --git a/llvm/lib/IR/Instructions.cpp b/llvm/lib/IR/Instructions.cpp
>>> index 6b33c03d66b70..6666d74ecabb3 100644
>>> --- a/llvm/lib/IR/Instructions.cpp
>>> +++ b/llvm/lib/IR/Instructions.cpp
>>> @@ -437,6 +437,35 @@ CallBase::BundleOpInfo
>>> &CallBase::getBundleOpInfoForOperand(unsigned OpIdx) {
>>> return *Current;
>>> }
>>>
>>> +CallBase *CallBase::addOperandBundle(CallBase *CB, uint32_t ID,
>>> + OperandBundleDef OB,
>>> + Instruction *InsertPt) {
>>> + if (CB->getOperandBundle(ID))
>>> + return CB;
>>> +
>>> + SmallVector<OperandBundleDef, 1> Bundles;
>>> + CB->getOperandBundlesAsDefs(Bundles);
>>> + Bundles.push_back(OB);
>>> + return Create(CB, Bundles, InsertPt);
>>> +}
>>> +
>>> +CallBase *CallBase::removeOperandBundle(CallBase *CB, uint32_t ID,
>>> + Instruction *InsertPt) {
>>> + SmallVector<OperandBundleDef, 1> Bundles;
>>> + bool CreateNew = false;
>>> +
>>> + for (unsigned I = 0, E = CB->getNumOperandBundles(); I != E; ++I) {
>>> + auto Bundle = CB->getOperandBundleAt(I);
>>> + if (Bundle.getTagID() == ID) {
>>> + CreateNew = true;
>>> + continue;
>>> + }
>>> + Bundles.emplace_back(Bundle);
>>> + }
>>> +
>>> + return CreateNew ? Create(CB, Bundles, InsertPt) : CB;
>>> +}
>>> +
>>>
>>> //===----------------------------------------------------------------------===//
>>> // CallInst Implementation
>>>
>>> //===----------------------------------------------------------------------===//
>>>
>>> diff --git a/llvm/lib/IR/LLVMContext.cpp b/llvm/lib/IR/LLVMContext.cpp
>>> index f554c54196bed..79002fb1b1bc7 100644
>>> --- a/llvm/lib/IR/LLVMContext.cpp
>>> +++ b/llvm/lib/IR/LLVMContext.cpp
>>> @@ -78,6 +78,12 @@ LLVMContext::LLVMContext() : pImpl(new
>>> LLVMContextImpl(*this)) {
>>> "gc-transition operand bundle id drifted!");
>>> (void)GCLiveEntry;
>>>
>>> + auto *ClangAttachedCall =
>>> + pImpl->getOrInsertBundleTag("clang.arc.attachedcall");
>>> + assert(ClangAttachedCall->second ==
>>> LLVMContext::OB_clang_arc_attachedcall &&
>>> + "clang.arc.attachedcall operand bundle id drifted!");
>>> + (void)ClangAttachedCall;
>>> +
>>> SyncScope::ID SingleThreadSSID =
>>> pImpl->getOrInsertSyncScopeID("singlethread");
>>> assert(SingleThreadSSID == SyncScope::SingleThread &&
>>>
>>> diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
>>> index f8db3b74dbb48..47bfbfb19524b 100644
>>> --- a/llvm/lib/IR/Verifier.cpp
>>> +++ b/llvm/lib/IR/Verifier.cpp
>>> @@ -3187,7 +3187,8 @@ void Verifier::visitCallBase(CallBase &Call) {
>>> // and at most one "preallocated" operand bundle.
>>> bool FoundDeoptBundle = false, FoundFuncletBundle = false,
>>> FoundGCTransitionBundle = false, FoundCFGuardTargetBundle =
>>> false,
>>> - FoundPreallocatedBundle = false, FoundGCLiveBundle = false;;
>>> + FoundPreallocatedBundle = false, FoundGCLiveBundle = false,
>>> + FoundAttachedCallBundle = false;
>>> for (unsigned i = 0, e = Call.getNumOperandBundles(); i < e; ++i) {
>>> OperandBundleUse BU = Call.getOperandBundleAt(i);
>>> uint32_t Tag = BU.getTagID();
>>> @@ -3228,9 +3229,19 @@ void Verifier::visitCallBase(CallBase &Call) {
>>> Assert(!FoundGCLiveBundle, "Multiple gc-live operand bundles",
>>> Call);
>>> FoundGCLiveBundle = true;
>>> + } else if (Tag == LLVMContext::OB_clang_arc_attachedcall) {
>>> + Assert(!FoundAttachedCallBundle,
>>> + "Multiple \"clang.arc.attachedcall\" operand bundles",
>>> Call);
>>> + FoundAttachedCallBundle = true;
>>> }
>>> }
>>>
>>> + if (FoundAttachedCallBundle)
>>> + Assert(FTy->getReturnType()->isPointerTy(),
>>> + "a call with operand bundle \"clang.arc.attachedcall\" must
>>> call a "
>>> + "function returning a pointer",
>>> + 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.
>>>
>>> diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
>>> b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
>>> index 703045412302f..3cc809eb255c3 100644
>>> --- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
>>> +++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
>>> @@ -29,6 +29,7 @@
>>> #include "llvm/ADT/StringRef.h"
>>> #include "llvm/ADT/Triple.h"
>>> #include "llvm/ADT/Twine.h"
>>> +#include "llvm/Analysis/ObjCARCUtil.h"
>>> #include "llvm/Analysis/VectorUtils.h"
>>> #include "llvm/CodeGen/CallingConvLower.h"
>>> #include "llvm/CodeGen/MachineBasicBlock.h"
>>> @@ -5711,11 +5712,12 @@
>>> AArch64TargetLowering::LowerCall(CallLoweringInfo &CLI,
>>> }
>>>
>>> unsigned CallOpc = AArch64ISD::CALL;
>>> - // Calls marked with "rv_marker" are special. They should be expanded
>>> to the
>>> - // call, directly followed by a special marker sequence. Use the
>>> CALL_RVMARKER
>>> - // to do that.
>>> - if (CLI.CB && CLI.CB->hasRetAttr("rv_marker")) {
>>> - assert(!IsTailCall && "tail calls cannot be marked with rv_marker");
>>> + // Calls with operand bundle "clang.arc.attachedcall" are special.
>>> They should
>>> + // be expanded to the call, directly followed by a special marker
>>> sequence.
>>> + // Use the CALL_RVMARKER to do that.
>>> + if (CLI.CB && objcarc::hasAttachedCallOpBundle(CLI.CB)) {
>>> + assert(!IsTailCall &&
>>> + "tail calls cannot be marked with clang.arc.attachedcall");
>>> CallOpc = AArch64ISD::CALL_RVMARKER;
>>> }
>>>
>>>
>>> diff --git a/llvm/lib/Transforms/ObjCARC/ARCRuntimeEntryPoints.h
>>> b/llvm/lib/Transforms/ObjCARC/ARCRuntimeEntryPoints.h
>>> index 258dc92408150..764dc5f927073 100644
>>> --- a/llvm/lib/Transforms/ObjCARC/ARCRuntimeEntryPoints.h
>>> +++ b/llvm/lib/Transforms/ObjCARC/ARCRuntimeEntryPoints.h
>>> @@ -42,6 +42,7 @@ enum class ARCRuntimeEntryPointKind {
>>> Autorelease,
>>> StoreStrong,
>>> RetainRV,
>>> + ClaimRV,
>>> RetainAutorelease,
>>> RetainAutoreleaseRV,
>>> };
>>> @@ -61,6 +62,7 @@ class ARCRuntimeEntryPoints {
>>> Autorelease = nullptr;
>>> StoreStrong = nullptr;
>>> RetainRV = nullptr;
>>> + ClaimRV = nullptr;
>>> RetainAutorelease = nullptr;
>>> RetainAutoreleaseRV = nullptr;
>>> }
>>> @@ -85,6 +87,9 @@ class ARCRuntimeEntryPoints {
>>> case ARCRuntimeEntryPointKind::RetainRV:
>>> return getIntrinsicEntryPoint(RetainRV,
>>>
>>> Intrinsic::objc_retainAutoreleasedReturnValue);
>>> + case ARCRuntimeEntryPointKind::ClaimRV:
>>> + return getIntrinsicEntryPoint(
>>> + ClaimRV, Intrinsic::objc_unsafeClaimAutoreleasedReturnValue);
>>> case ARCRuntimeEntryPointKind::RetainAutorelease:
>>> return getIntrinsicEntryPoint(RetainAutorelease,
>>> Intrinsic::objc_retainAutorelease);
>>> @@ -121,6 +126,9 @@ class ARCRuntimeEntryPoints {
>>> /// Declaration for objc_retainAutoreleasedReturnValue().
>>> Function *RetainRV = nullptr;
>>>
>>> + /// Declaration for objc_unsafeClaimAutoreleasedReturnValue().
>>> + Function *ClaimRV = nullptr;
>>> +
>>> /// Declaration for objc_retainAutorelease().
>>> Function *RetainAutorelease = nullptr;
>>>
>>>
>>> diff --git a/llvm/lib/Transforms/ObjCARC/ObjCARC.cpp
>>> b/llvm/lib/Transforms/ObjCARC/ObjCARC.cpp
>>> index 970136392fdd9..06b12149f5971 100644
>>> --- a/llvm/lib/Transforms/ObjCARC/ObjCARC.cpp
>>> +++ b/llvm/lib/Transforms/ObjCARC/ObjCARC.cpp
>>> @@ -14,7 +14,12 @@
>>>
>>> #include "ObjCARC.h"
>>> #include "llvm-c/Initialization.h"
>>> +#include "llvm/Analysis/ObjCARCUtil.h"
>>> +#include "llvm/IR/IRBuilder.h"
>>> +#include "llvm/IR/InlineAsm.h"
>>> +#include "llvm/IR/Instructions.h"
>>> #include "llvm/InitializePasses.h"
>>> +#include "llvm/Transforms/Utils/BasicBlockUtils.h"
>>>
>>> namespace llvm {
>>> class PassRegistry;
>>> @@ -37,3 +42,91 @@ void llvm::initializeObjCARCOpts(PassRegistry
>>> &Registry) {
>>> void LLVMInitializeObjCARCOpts(LLVMPassRegistryRef R) {
>>> initializeObjCARCOpts(*unwrap(R));
>>> }
>>> +
>>> +CallInst *objcarc::createCallInstWithColors(
>>> + FunctionCallee Func, ArrayRef<Value *> Args, const Twine &NameStr,
>>> + Instruction *InsertBefore,
>>> + const DenseMap<BasicBlock *, ColorVector> &BlockColors) {
>>> + FunctionType *FTy = Func.getFunctionType();
>>> + Value *Callee = Func.getCallee();
>>> + SmallVector<OperandBundleDef, 1> OpBundles;
>>> +
>>> + if (!BlockColors.empty()) {
>>> + const ColorVector &CV =
>>> BlockColors.find(InsertBefore->getParent())->second;
>>> + assert(CV.size() == 1 && "non-unique color for block!");
>>> + Instruction *EHPad = CV.front()->getFirstNonPHI();
>>> + if (EHPad->isEHPad())
>>> + OpBundles.emplace_back("funclet", EHPad);
>>> + }
>>> +
>>> + return CallInst::Create(FTy, Callee, Args, OpBundles, NameStr,
>>> InsertBefore);
>>> +}
>>> +
>>> +std::pair<bool, bool>
>>> +BundledRetainClaimRVs::insertAfterInvokes(Function &F, DominatorTree
>>> *DT) {
>>> + bool Changed = false, CFGChanged = false;
>>> +
>>> + for (BasicBlock &BB : F) {
>>> + auto *I = dyn_cast<InvokeInst>(BB.getTerminator());
>>> +
>>> + if (!I)
>>> + continue;
>>> +
>>> + if (!objcarc::hasAttachedCallOpBundle(I))
>>> + continue;
>>> +
>>> + BasicBlock *DestBB = I->getNormalDest();
>>> +
>>> + if (!DestBB->getSinglePredecessor()) {
>>> + assert(I->getSuccessor(0) == DestBB &&
>>> + "the normal dest is expected to be the first successor");
>>> + DestBB = SplitCriticalEdge(I, 0,
>>> CriticalEdgeSplittingOptions(DT));
>>> + CFGChanged = true;
>>> + }
>>> +
>>> + // We don't have to call insertRVCallWithColors since DestBB is the
>>> normal
>>> + // destination of the invoke.
>>> + insertRVCall(&*DestBB->getFirstInsertionPt(), I);
>>> + Changed = true;
>>> + }
>>> +
>>> + return std::make_pair(Changed, CFGChanged);
>>> +}
>>> +
>>> +CallInst *BundledRetainClaimRVs::insertRVCall(Instruction *InsertPt,
>>> + CallBase *AnnotatedCall) {
>>> + DenseMap<BasicBlock *, ColorVector> BlockColors;
>>> + return insertRVCallWithColors(InsertPt, AnnotatedCall, BlockColors);
>>> +}
>>> +
>>> +CallInst *BundledRetainClaimRVs::insertRVCallWithColors(
>>> + Instruction *InsertPt, CallBase *AnnotatedCall,
>>> + const DenseMap<BasicBlock *, ColorVector> &BlockColors) {
>>> + IRBuilder<> Builder(InsertPt);
>>> + bool IsRetainRV = objcarc::hasAttachedCallOpBundle(AnnotatedCall,
>>> true);
>>> + Function *Func = EP.get(IsRetainRV ?
>>> ARCRuntimeEntryPointKind::RetainRV
>>> + :
>>> ARCRuntimeEntryPointKind::ClaimRV);
>>> + Type *ParamTy = Func->getArg(0)->getType();
>>> + Value *CallArg = Builder.CreateBitCast(AnnotatedCall, ParamTy);
>>> + auto *Call =
>>> + createCallInstWithColors(Func, CallArg, "", InsertPt,
>>> BlockColors);
>>> + RVCalls[Call] = AnnotatedCall;
>>> + return Call;
>>> +}
>>> +
>>> +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<CallInst>(P.second))
>>> + CI->setTailCallKind(CallInst::TCK_NoTail);
>>> + } else {
>>> + for (auto P : RVCalls)
>>> + EraseInstruction(P.first);
>>> + }
>>> +
>>> + RVCalls.clear();
>>> +}
>>>
>>> diff --git a/llvm/lib/Transforms/ObjCARC/ObjCARC.h
>>> b/llvm/lib/Transforms/ObjCARC/ObjCARC.h
>>> index 8227a8c6f75f0..1f9d76969bfd7 100644
>>> --- a/llvm/lib/Transforms/ObjCARC/ObjCARC.h
>>> +++ b/llvm/lib/Transforms/ObjCARC/ObjCARC.h
>>> @@ -22,7 +22,10 @@
>>> #ifndef LLVM_LIB_TRANSFORMS_OBJCARC_OBJCARC_H
>>> #define LLVM_LIB_TRANSFORMS_OBJCARC_OBJCARC_H
>>>
>>> +#include "ARCRuntimeEntryPoints.h"
>>> +#include "llvm/Analysis/EHPersonalities.h"
>>> #include "llvm/Analysis/ObjCARCAnalysisUtils.h"
>>> +#include "llvm/Analysis/ObjCARCUtil.h"
>>> #include "llvm/Transforms/Utils/Local.h"
>>>
>>> namespace llvm {
>>> @@ -87,6 +90,75 @@ void getEquivalentPHIs(PHINodeTy &PN, VectorTy
>>> &PHIList) {
>>> }
>>> }
>>>
>>> +static inline MDString *getRVInstMarker(Module &M) {
>>> + const char *MarkerKey = getRVMarkerModuleFlagStr();
>>> + return dyn_cast_or_null<MDString>(M.getModuleFlag(MarkerKey));
>>> +}
>>> +
>>> +/// Create a call instruction with the correct funclet token. This
>>> should be
>>> +/// called instead of calling CallInst::Create directly unless the call
>>> is
>>> +/// going to be removed from the IR before WinEHPrepare.
>>> +CallInst *createCallInstWithColors(
>>> + FunctionCallee Func, ArrayRef<Value *> Args, const Twine &NameStr,
>>> + Instruction *InsertBefore,
>>> + const DenseMap<BasicBlock *, ColorVector> &BlockColors);
>>> +
>>> +class BundledRetainClaimRVs {
>>> +public:
>>> + BundledRetainClaimRVs(ARCRuntimeEntryPoints &P, bool ContractPass)
>>> + : EP(P), ContractPass(ContractPass) {}
>>> + ~BundledRetainClaimRVs();
>>> +
>>> + /// Insert a retainRV/claimRV call to the normal destination blocks
>>> of invokes
>>> + /// with operand bundle "clang.arc.attachedcall". If the edge to the
>>> normal
>>> + /// destination block is a critical edge, split it.
>>> + std::pair<bool, bool> insertAfterInvokes(Function &F, DominatorTree
>>> *DT);
>>> +
>>> + /// Insert a retainRV/claimRV call.
>>> + CallInst *insertRVCall(Instruction *InsertPt, CallBase
>>> *AnnotatedCall);
>>> +
>>> + /// Insert a retainRV/claimRV call with colors.
>>> + CallInst *insertRVCallWithColors(
>>> + Instruction *InsertPt, CallBase *AnnotatedCall,
>>> + const DenseMap<BasicBlock *, ColorVector> &BlockColors);
>>> +
>>> + /// See if an instruction is a bundled retainRV/claimRV call.
>>> + bool contains(const Instruction *I) const {
>>> + if (auto *CI = dyn_cast<CallInst>(I))
>>> + return RVCalls.count(CI);
>>> + return false;
>>> + }
>>> +
>>> + /// Remove a retainRV/claimRV call entirely.
>>> + void eraseInst(CallInst *CI) {
>>> + auto It = RVCalls.find(CI);
>>> + if (It != RVCalls.end()) {
>>> + // Remove call to @llvm.objc.clang.arc.noop.use.
>>> + for (auto U = It->second->user_begin(), E =
>>> It->second->user_end(); U != E; ++U)
>>> + if (auto *CI = dyn_cast<CallInst>(*U))
>>> + if (CI->getIntrinsicID() ==
>>> Intrinsic::objc_clang_arc_noop_use) {
>>> + CI->eraseFromParent();
>>> + break;
>>> + }
>>> +
>>> + auto *NewCall = CallBase::removeOperandBundle(
>>> + It->second, LLVMContext::OB_clang_arc_attachedcall,
>>> It->second);
>>> + NewCall->copyMetadata(*It->second);
>>> + It->second->replaceAllUsesWith(NewCall);
>>> + It->second->eraseFromParent();
>>> + RVCalls.erase(It);
>>> + }
>>> + EraseInstruction(CI);
>>> + }
>>> +
>>> +private:
>>> + /// A map of inserted retainRV/claimRV calls to annotated
>>> calls/invokes.
>>> + DenseMap<CallInst *, CallBase *> RVCalls;
>>> +
>>> + ARCRuntimeEntryPoints &EP;
>>> + bool ContractPass;
>>> +};
>>> +
>>> } // end namespace objcarc
>>> } // end namespace llvm
>>>
>>>
>>> diff --git a/llvm/lib/Transforms/ObjCARC/ObjCARCContract.cpp
>>> b/llvm/lib/Transforms/ObjCARC/ObjCARCContract.cpp
>>> index 86d161116e8c7..62161b5b6b40a 100644
>>> --- a/llvm/lib/Transforms/ObjCARC/ObjCARCContract.cpp
>>> +++ b/llvm/lib/Transforms/ObjCARC/ObjCARCContract.cpp
>>> @@ -32,6 +32,7 @@
>>> #include "llvm/ADT/Statistic.h"
>>> #include "llvm/Analysis/AliasAnalysis.h"
>>> #include "llvm/Analysis/EHPersonalities.h"
>>> +#include "llvm/Analysis/ObjCARCUtil.h"
>>> #include "llvm/IR/Dominators.h"
>>> #include "llvm/IR/InlineAsm.h"
>>> #include "llvm/IR/InstIterator.h"
>>> @@ -63,13 +64,12 @@ namespace {
>>>
>>> class ObjCARCContract {
>>> bool Changed;
>>> + bool CFGChanged;
>>> AAResults *AA;
>>> DominatorTree *DT;
>>> ProvenanceAnalysis PA;
>>> ARCRuntimeEntryPoints EP;
>>> -
>>> - /// A flag indicating whether this optimization pass should run.
>>> - bool Run;
>>> + BundledRetainClaimRVs *BundledInsts = nullptr;
>>>
>>> /// The inline asm string to insert between calls and RetainRV calls
>>> to make
>>> /// the optimization work on targets which need it.
>>> @@ -98,6 +98,7 @@ class ObjCARCContract {
>>> public:
>>> bool init(Module &M);
>>> bool run(Function &F, AAResults *AA, DominatorTree *DT);
>>> + bool hasCFGChanged() const { return CFGChanged; }
>>> };
>>>
>>> class ObjCARCContractLegacyPass : public FunctionPass {
>>> @@ -304,32 +305,6 @@ findRetainForStoreStrongContraction(Value *New,
>>> StoreInst *Store,
>>> return Retain;
>>> }
>>>
>>> -/// Create a call instruction with the correct funclet token. Should be
>>> used
>>> -/// instead of calling CallInst::Create directly.
>>> -static CallInst *
>>> -createCallInst(FunctionType *FTy, Value *Func, ArrayRef<Value *> Args,
>>> - const Twine &NameStr, Instruction *InsertBefore,
>>> - const DenseMap<BasicBlock *, ColorVector> &BlockColors) {
>>> - SmallVector<OperandBundleDef, 1> OpBundles;
>>> - if (!BlockColors.empty()) {
>>> - const ColorVector &CV =
>>> BlockColors.find(InsertBefore->getParent())->second;
>>> - assert(CV.size() == 1 && "non-unique color for block!");
>>> - Instruction *EHPad = CV.front()->getFirstNonPHI();
>>> - if (EHPad->isEHPad())
>>> - OpBundles.emplace_back("funclet", EHPad);
>>> - }
>>> -
>>> - return CallInst::Create(FTy, Func, Args, OpBundles, NameStr,
>>> InsertBefore);
>>> -}
>>> -
>>> -static CallInst *
>>> -createCallInst(FunctionCallee Func, ArrayRef<Value *> Args, const Twine
>>> &NameStr,
>>> - Instruction *InsertBefore,
>>> - const DenseMap<BasicBlock *, ColorVector> &BlockColors) {
>>> - return createCallInst(Func.getFunctionType(), Func.getCallee(), Args,
>>> NameStr,
>>> - InsertBefore, BlockColors);
>>> -}
>>> -
>>> /// Attempt to merge an objc_release with a store, load, and
>>> objc_retain to form
>>> /// an objc_storeStrong. An objc_storeStrong:
>>> ///
>>> @@ -411,7 +386,8 @@ void
>>> ObjCARCContract::tryToContractReleaseIntoStoreStrong(
>>> if (Args[1]->getType() != I8X)
>>> Args[1] = new BitCastInst(Args[1], I8X, "", Store);
>>> Function *Decl = EP.get(ARCRuntimeEntryPointKind::StoreStrong);
>>> - CallInst *StoreStrong = createCallInst(Decl, Args, "", Store,
>>> BlockColors);
>>> + CallInst *StoreStrong =
>>> + objcarc::createCallInstWithColors(Decl, Args, "", Store,
>>> BlockColors);
>>> StoreStrong->setDoesNotThrow();
>>> StoreStrong->setDebugLoc(Store->getDebugLoc());
>>>
>>> @@ -456,9 +432,14 @@ bool ObjCARCContract::tryToPeepholeInstruction(
>>> case ARCInstKind::RetainRV:
>>> case ARCInstKind::ClaimRV: {
>>> // If we're compiling for a target which needs a special inline-asm
>>> - // marker to do the return value optimization, insert it now.
>>> + // marker to do the return value optimization and the
>>> retainRV/claimRV call
>>> + // wasn't bundled with a call, insert the marker now.
>>> if (!RVInstMarker)
>>> return false;
>>> +
>>> + if (BundledInsts->contains(Inst))
>>> + return false;
>>> +
>>> BasicBlock::iterator BBI = Inst->getIterator();
>>> BasicBlock *InstParent = Inst->getParent();
>>>
>>> @@ -486,7 +467,7 @@ bool ObjCARCContract::tryToPeepholeInstruction(
>>> RVInstMarker->getString(),
>>> /*Constraints=*/"", /*hasSideEffects=*/true);
>>>
>>> - createCallInst(IA, None, "", Inst, BlockColors);
>>> + objcarc::createCallInstWithColors(IA, None, "", Inst,
>>> BlockColors);
>>> }
>>> decline_rv_optimization:
>>> return false;
>>> @@ -525,6 +506,12 @@ bool ObjCARCContract::tryToPeepholeInstruction(
>>> Inst->eraseFromParent();
>>> return true;
>>> default:
>>> + if (auto *CI = dyn_cast<CallInst>(Inst))
>>> + if (CI->getIntrinsicID() == Intrinsic::objc_clang_arc_noop_use) {
>>> + // Remove calls to @llvm.objc.clang.arc.noop.use(...).
>>> + Changed = true;
>>> + CI->eraseFromParent();
>>> + }
>>> return true;
>>> }
>>> }
>>> @@ -534,16 +521,10 @@ bool ObjCARCContract::tryToPeepholeInstruction(
>>>
>>> //===----------------------------------------------------------------------===//
>>>
>>> bool ObjCARCContract::init(Module &M) {
>>> - // If nothing in the Module uses ARC, don't do anything.
>>> - Run = ModuleHasARC(M);
>>> - if (!Run)
>>> - return false;
>>> -
>>> EP.init(&M);
>>>
>>> // Initialize RVInstMarker.
>>> - const char *MarkerKey =
>>> "clang.arc.retainAutoreleasedReturnValueMarker";
>>> - RVInstMarker = dyn_cast_or_null<MDString>(M.getModuleFlag(MarkerKey));
>>> + RVInstMarker = getRVInstMarker(M);
>>>
>>> return false;
>>> }
>>> @@ -552,14 +533,16 @@ bool ObjCARCContract::run(Function &F, AAResults
>>> *A, DominatorTree *D) {
>>> if (!EnableARCOpts)
>>> return false;
>>>
>>> - // If nothing in the Module uses ARC, don't do anything.
>>> - if (!Run)
>>> - return false;
>>> -
>>> - Changed = false;
>>> + Changed = CFGChanged = false;
>>> AA = A;
>>> DT = D;
>>> PA.setAA(A);
>>> + BundledRetainClaimRVs BRV(EP, true);
>>> + BundledInsts = &BRV;
>>> +
>>> + std::pair<bool, bool> R = BundledInsts->insertAfterInvokes(F, DT);
>>> + Changed |= R.first;
>>> + CFGChanged |= R.second;
>>>
>>> DenseMap<BasicBlock *, ColorVector> BlockColors;
>>> if (F.hasPersonalityFn() &&
>>> @@ -584,6 +567,13 @@ bool ObjCARCContract::run(Function &F, AAResults
>>> *A, DominatorTree *D) {
>>>
>>> LLVM_DEBUG(dbgs() << "Visiting: " << *Inst << "\n");
>>>
>>> + if (auto *CI = dyn_cast<CallInst>(Inst))
>>> + if (objcarc::hasAttachedCallOpBundle(CI)) {
>>> + BundledInsts->insertRVCallWithColors(&*I, CI, BlockColors);
>>> + --I;
>>> + Changed = true;
>>> + }
>>> +
>>> // First try to peephole Inst. If there is nothing further we can
>>> do in
>>> // terms of undoing objc-arc-expand, process the next inst.
>>> if (tryToPeepholeInstruction(F, Inst, I, TailOkForStoreStrongs,
>>> @@ -733,7 +723,6 @@ INITIALIZE_PASS_END(ObjCARCContractLegacyPass,
>>> "objc-arc-contract",
>>> void ObjCARCContractLegacyPass::getAnalysisUsage(AnalysisUsage &AU)
>>> const {
>>> AU.addRequired<AAResultsWrapperPass>();
>>> AU.addRequired<DominatorTreeWrapperPass>();
>>> - AU.setPreservesCFG();
>>> }
>>>
>>> Pass *llvm::createObjCARCContractPass() {
>>> @@ -757,9 +746,11 @@ PreservedAnalyses ObjCARCContractPass::run(Function
>>> &F,
>>>
>>> bool Changed = OCAC.run(F, &AM.getResult<AAManager>(F),
>>> &AM.getResult<DominatorTreeAnalysis>(F));
>>> + bool CFGChanged = OCAC.hasCFGChanged();
>>> if (Changed) {
>>> PreservedAnalyses PA;
>>> - PA.preserveSet<CFGAnalyses>();
>>> + if (!CFGChanged)
>>> + PA.preserveSet<CFGAnalyses>();
>>> return PA;
>>> }
>>> return PreservedAnalyses::all();
>>>
>>> diff --git a/llvm/lib/Transforms/ObjCARC/ObjCARCOpts.cpp
>>> b/llvm/lib/Transforms/ObjCARC/ObjCARCOpts.cpp
>>> index 2043a7bfc8876..0769d466be93d 100644
>>> --- a/llvm/lib/Transforms/ObjCARC/ObjCARCOpts.cpp
>>> +++ b/llvm/lib/Transforms/ObjCARC/ObjCARCOpts.cpp
>>> @@ -41,6 +41,7 @@
>>> #include "llvm/Analysis/ObjCARCAliasAnalysis.h"
>>> #include "llvm/Analysis/ObjCARCAnalysisUtils.h"
>>> #include "llvm/Analysis/ObjCARCInstKind.h"
>>> +#include "llvm/Analysis/ObjCARCUtil.h"
>>> #include "llvm/IR/BasicBlock.h"
>>> #include "llvm/IR/CFG.h"
>>> #include "llvm/IR/Constant.h"
>>> @@ -483,6 +484,7 @@ namespace {
>>> /// The main ARC optimization pass.
>>> class ObjCARCOpt {
>>> bool Changed;
>>> + bool CFGChanged;
>>> ProvenanceAnalysis PA;
>>>
>>> /// A cache of references to runtime entry point constants.
>>> @@ -492,8 +494,7 @@ class ObjCARCOpt {
>>> /// MDKind identifiers.
>>> ARCMDKindCache MDKindCache;
>>>
>>> - /// A flag indicating whether this optimization pass should run.
>>> - bool Run;
>>> + BundledRetainClaimRVs *BundledInsts = nullptr;
>>>
>>> /// A flag indicating whether the optimization that removes or moves
>>> /// retain/release pairs should be performed.
>>> @@ -573,6 +574,7 @@ class ObjCARCOpt {
>>> void init(Module &M);
>>> bool run(Function &F, AAResults &AA);
>>> void releaseMemory();
>>> + bool hasCFGChanged() const { return CFGChanged; }
>>> };
>>>
>>> /// The main ARC optimization pass.
>>> @@ -610,8 +612,6 @@ Pass *llvm::createObjCARCOptPass() { return new
>>> ObjCARCOptLegacyPass(); }
>>> void ObjCARCOptLegacyPass::getAnalysisUsage(AnalysisUsage &AU) const {
>>> AU.addRequired<ObjCARCAAWrapperPass>();
>>> AU.addRequired<AAResultsWrapperPass>();
>>> - // ARC optimization doesn't currently split critical edges.
>>> - AU.setPreservesCFG();
>>> }
>>>
>>> /// Turn objc_retainAutoreleasedReturnValue into objc_retain if the
>>> operand is
>>> @@ -640,6 +640,9 @@ ObjCARCOpt::OptimizeRetainRVCall(Function &F,
>>> Instruction *RetainRV) {
>>> }
>>> }
>>>
>>> + assert(!BundledInsts->contains(RetainRV) &&
>>> + "a bundled retainRV's argument should be a call");
>>> +
>>> // Turn it to a plain objc_retain.
>>> Changed = true;
>>> ++NumPeeps;
>>> @@ -661,6 +664,9 @@ bool ObjCARCOpt::OptimizeInlinedAutoreleaseRVCall(
>>> Function &F, DenseMap<BasicBlock *, ColorVector> &BlockColors,
>>> Instruction *Inst, const Value *&Arg, ARCInstKind Class,
>>> Instruction *AutoreleaseRV, const Value *&AutoreleaseRVArg) {
>>> + if (BundledInsts->contains(Inst))
>>> + return false;
>>> +
>>> // Must be in the same basic block.
>>> assert(Inst->getParent() == AutoreleaseRV->getParent());
>>>
>>> @@ -844,6 +850,12 @@ void ObjCARCOpt::OptimizeIndividualCalls(Function
>>> &F) {
>>> for (inst_iterator I = inst_begin(&F), E = inst_end(&F); I != E; ) {
>>> Instruction *Inst = &*I++;
>>>
>>> + if (auto *CI = dyn_cast<CallInst>(Inst))
>>> + if (objcarc::hasAttachedCallOpBundle(CI)) {
>>> + BundledInsts->insertRVCall(&*I, CI);
>>> + Changed = true;
>>> + }
>>> +
>>> ARCInstKind Class = GetBasicARCInstKind(Inst);
>>>
>>> // Skip this loop if this instruction isn't itself an ARC intrinsic.
>>> @@ -922,6 +934,11 @@ void ObjCARCOpt::OptimizeIndividualCallImpl(
>>> // We can delete this call if it takes an inert value.
>>> SmallPtrSet<Value *, 1> VisitedPhis;
>>>
>>> + if (BundledInsts->contains(Inst)) {
>>> + UsedInThisFunction |= 1 << unsigned(Class);
>>> + return;
>>> + }
>>> +
>>> if (IsNoopOnGlobal(Class))
>>> if (isInertARCValue(Inst->getOperand(0), VisitedPhis)) {
>>> if (!Inst->getType()->isVoidTy())
>>> @@ -1539,7 +1556,7 @@ ObjCARCOpt::VisitInstructionTopDown(Instruction
>>> *Inst,
>>> if (Ptr == Arg)
>>> continue; // Handled above.
>>> TopDownPtrState &S = MI->second;
>>> - if (S.HandlePotentialAlterRefCount(Inst, Ptr, PA, Class))
>>> + if (S.HandlePotentialAlterRefCount(Inst, Ptr, PA, Class,
>>> *BundledInsts))
>>> continue;
>>>
>>> S.HandlePotentialUse(Inst, Ptr, PA, Class);
>>> @@ -2340,7 +2357,7 @@ void ObjCARCOpt::OptimizeReturns(Function &F) {
>>> ++NumRets;
>>> LLVM_DEBUG(dbgs() << "Erasing: " << *Retain << "\nErasing: " <<
>>> *Autorelease
>>> << "\n");
>>> - EraseInstruction(Retain);
>>> + BundledInsts->eraseInst(Retain);
>>> EraseInstruction(Autorelease);
>>> }
>>> }
>>> @@ -2373,11 +2390,6 @@ void ObjCARCOpt::init(Module &M) {
>>> if (!EnableARCOpts)
>>> return;
>>>
>>> - // If nothing in the Module uses ARC, don't do anything.
>>> - Run = ModuleHasARC(M);
>>> - if (!Run)
>>> - return;
>>> -
>>> // Intuitively, objc_retain and others are nocapture, however in
>>> practice
>>> // they are not, because they return their argument value. And
>>> objc_release
>>> // calls finalizers which can have arbitrary side effects.
>>> @@ -2391,16 +2403,18 @@ bool ObjCARCOpt::run(Function &F, AAResults &AA)
>>> {
>>> if (!EnableARCOpts)
>>> return false;
>>>
>>> - // If nothing in the Module uses ARC, don't do anything.
>>> - if (!Run)
>>> - return false;
>>> -
>>> - Changed = false;
>>> + Changed = CFGChanged = false;
>>> + BundledRetainClaimRVs BRV(EP, false);
>>> + BundledInsts = &BRV;
>>>
>>> LLVM_DEBUG(dbgs() << "<<< ObjCARCOpt: Visiting Function: " <<
>>> F.getName()
>>> << " >>>"
>>> "\n");
>>>
>>> + std::pair<bool, bool> R = BundledInsts->insertAfterInvokes(F,
>>> nullptr);
>>> + Changed |= R.first;
>>> + CFGChanged |= R.second;
>>> +
>>> PA.setAA(&AA);
>>>
>>> #ifndef NDEBUG
>>> @@ -2465,9 +2479,11 @@ PreservedAnalyses ObjCARCOptPass::run(Function &F,
>>> OCAO.init(*F.getParent());
>>>
>>> bool Changed = OCAO.run(F, AM.getResult<AAManager>(F));
>>> + bool CFGChanged = OCAO.hasCFGChanged();
>>> if (Changed) {
>>> PreservedAnalyses PA;
>>> - PA.preserveSet<CFGAnalyses>();
>>> + if (!CFGChanged)
>>> + PA.preserveSet<CFGAnalyses>();
>>> return PA;
>>> }
>>> return PreservedAnalyses::all();
>>>
>>> diff --git a/llvm/lib/Transforms/ObjCARC/PtrState.cpp
>>> b/llvm/lib/Transforms/ObjCARC/PtrState.cpp
>>> index 66fdca062b3d5..d10d5851d5ea0 100644
>>> --- a/llvm/lib/Transforms/ObjCARC/PtrState.cpp
>>> +++ b/llvm/lib/Transforms/ObjCARC/PtrState.cpp
>>> @@ -11,6 +11,7 @@
>>> #include "ObjCARC.h"
>>> #include "llvm/Analysis/ObjCARCAnalysisUtils.h"
>>> #include "llvm/Analysis/ObjCARCInstKind.h"
>>> +#include "llvm/Analysis/ObjCARCUtil.h"
>>> #include "llvm/IR/BasicBlock.h"
>>> #include "llvm/IR/Instruction.h"
>>> #include "llvm/IR/Instructions.h"
>>> @@ -276,6 +277,13 @@ void
>>> BottomUpPtrState::HandlePotentialUse(BasicBlock *BB, Instruction *Inst,
>>> InsertAfter = skipDebugIntrinsics(InsertAfter);
>>>
>>> InsertReverseInsertPt(&*InsertAfter);
>>> +
>>> + // Don't insert anything between a call/invoke with operand bundle
>>> + // "clang.arc.attachedcall" and the retainRV/claimRV call that uses
>>> the call
>>> + // result.
>>> + if (auto *CB = dyn_cast<CallBase>(Inst))
>>> + if (objcarc::hasAttachedCallOpBundle(CB))
>>> + SetCFGHazardAfflicted(true);
>>> };
>>>
>>> // Check for possible direct uses.
>>> @@ -366,10 +374,9 @@ bool
>>> TopDownPtrState::MatchWithRelease(ARCMDKindCache &Cache,
>>> llvm_unreachable("Sequence unknown enum value");
>>> }
>>>
>>> -bool TopDownPtrState::HandlePotentialAlterRefCount(Instruction *Inst,
>>> - const Value *Ptr,
>>> - ProvenanceAnalysis
>>> &PA,
>>> - ARCInstKind Class) {
>>> +bool TopDownPtrState::HandlePotentialAlterRefCount(
>>> + Instruction *Inst, const Value *Ptr, ProvenanceAnalysis &PA,
>>> + ARCInstKind Class, const BundledRetainClaimRVs &BundledRVs) {
>>> // Check for possible releases. Treat clang.arc.use as a releasing
>>> instruction
>>> // to prevent sinking a retain past it.
>>> if (!CanDecrementRefCount(Inst, Ptr, PA, Class) &&
>>> @@ -385,6 +392,12 @@ bool
>>> TopDownPtrState::HandlePotentialAlterRefCount(Instruction *Inst,
>>> assert(!HasReverseInsertPts());
>>> InsertReverseInsertPt(Inst);
>>>
>>> + // Don't insert anything between a call/invoke with operand bundle
>>> + // "clang.arc.attachedcall" and the retainRV/claimRV call that uses
>>> the call
>>> + // result.
>>> + if (BundledRVs.contains(Inst))
>>> + SetCFGHazardAfflicted(true);
>>> +
>>> // One call can't cause a transition from S_Retain to S_CanRelease
>>> // and S_CanRelease to S_Use. If we've made the first transition,
>>> // we're done.
>>>
>>> diff --git a/llvm/lib/Transforms/ObjCARC/PtrState.h
>>> b/llvm/lib/Transforms/ObjCARC/PtrState.h
>>> index c102219b59c93..232db2bd33bc7 100644
>>> --- a/llvm/lib/Transforms/ObjCARC/PtrState.h
>>> +++ b/llvm/lib/Transforms/ObjCARC/PtrState.h
>>> @@ -31,6 +31,7 @@ class Value;
>>> namespace objcarc {
>>>
>>> class ARCMDKindCache;
>>> +class BundledRetainClaimRVs;
>>> class ProvenanceAnalysis;
>>>
>>> /// \enum Sequence
>>> @@ -201,7 +202,8 @@ struct TopDownPtrState : PtrState {
>>> ProvenanceAnalysis &PA, ARCInstKind Class);
>>>
>>> bool HandlePotentialAlterRefCount(Instruction *Inst, const Value *Ptr,
>>> - ProvenanceAnalysis &PA, ARCInstKind
>>> Class);
>>> + ProvenanceAnalysis &PA, ARCInstKind
>>> Class,
>>> + const BundledRetainClaimRVs
>>> &BundledRVs);
>>> };
>>>
>>> } // end namespace objcarc
>>>
>>> diff --git a/llvm/lib/Transforms/Scalar/SCCP.cpp
>>> b/llvm/lib/Transforms/Scalar/SCCP.cpp
>>> index de6be52adf216..045abefd4741c 100644
>>> --- a/llvm/lib/Transforms/Scalar/SCCP.cpp
>>> +++ b/llvm/lib/Transforms/Scalar/SCCP.cpp
>>> @@ -144,9 +144,8 @@ class SCCPSolver : public InstVisitor<SCCPSolver> {
>>> /// represented here for efficient lookup.
>>> SmallPtrSet<Function *, 16> MRVFunctionsTracked;
>>>
>>> - /// MustTailFunctions - Each function here is a callee of
>>> non-removable
>>> - /// musttail call site.
>>> - SmallPtrSet<Function *, 16> MustTailCallees;
>>> + /// A list of functions whose return cannot be modified.
>>> + SmallPtrSet<Function *, 16> MustPreserveReturnsInFunctions;
>>>
>>> /// TrackingIncomingArguments - This is the set of functions for whose
>>> /// arguments we make optimistic assumptions about and try to prove as
>>> @@ -238,16 +237,14 @@ class SCCPSolver : public InstVisitor<SCCPSolver> {
>>> TrackedRetVals.insert(std::make_pair(F, ValueLatticeElement()));
>>> }
>>>
>>> - /// AddMustTailCallee - If the SCCP solver finds that this function
>>> is called
>>> - /// from non-removable musttail call site.
>>> - void AddMustTailCallee(Function *F) {
>>> - MustTailCallees.insert(F);
>>> + /// Add function to the list of functions whose return cannot be
>>> modified.
>>> + void addToMustPreserveReturnsInFunctions(Function *F) {
>>> + MustPreserveReturnsInFunctions.insert(F);
>>> }
>>>
>>> - /// Returns true if the given function is called from non-removable
>>> musttail
>>> - /// call site.
>>> - bool isMustTailCallee(Function *F) {
>>> - return MustTailCallees.count(F);
>>> + /// Returns true if the return of the given function cannot be
>>> modified.
>>> + bool mustPreserveReturn(Function *F) {
>>> + return MustPreserveReturnsInFunctions.count(F);
>>> }
>>>
>>> void AddArgumentTrackedFunction(Function *F) {
>>> @@ -319,12 +316,6 @@ class SCCPSolver : public InstVisitor<SCCPSolver> {
>>> return MRVFunctionsTracked;
>>> }
>>>
>>> - /// getMustTailCallees - Get the set of functions which are called
>>> - /// from non-removable musttail call sites.
>>> - const SmallPtrSet<Function *, 16> getMustTailCallees() {
>>> - return MustTailCallees;
>>> - }
>>> -
>>> /// markOverdefined - Mark the specified value overdefined. This
>>> /// works with both scalars and structs.
>>> void markOverdefined(Value *V) {
>>> @@ -1650,16 +1641,19 @@ static bool tryToReplaceWithConstant(SCCPSolver
>>> &Solver, Value *V) {
>>> assert(Const && "Constant is nullptr here!");
>>>
>>> // Replacing `musttail` instructions with constant breaks `musttail`
>>> invariant
>>> - // unless the call itself can be removed
>>> - CallInst *CI = dyn_cast<CallInst>(V);
>>> - if (CI && CI->isMustTailCall() && !CI->isSafeToRemove()) {
>>> - Function *F = CI->getCalledFunction();
>>> + // unless the call itself can be removed.
>>> + // Calls with "clang.arc.attachedcall" implicitly use the return
>>> value and
>>> + // those uses cannot be updated with a constant.
>>> + CallBase *CB = dyn_cast<CallBase>(V);
>>> + if (CB && ((CB->isMustTailCall() && !CB->isSafeToRemove()) ||
>>> +
>>> CB->getOperandBundle(LLVMContext::OB_clang_arc_attachedcall))) {
>>> + Function *F = CB->getCalledFunction();
>>>
>>> // Don't zap returns of the callee
>>> if (F)
>>> - Solver.AddMustTailCallee(F);
>>> + Solver.addToMustPreserveReturnsInFunctions(F);
>>>
>>> - LLVM_DEBUG(dbgs() << " Can\'t treat the result of musttail call :
>>> " << *CI
>>> + LLVM_DEBUG(dbgs() << " Can\'t treat the result of call " << *CB
>>> << " as a constant\n");
>>> return false;
>>> }
>>> @@ -1821,11 +1815,12 @@ static void findReturnsToZap(Function &F,
>>> if (!Solver.isArgumentTrackedFunction(&F))
>>> return;
>>>
>>> - // There is a non-removable musttail call site of this function.
>>> Zapping
>>> - // returns is not allowed.
>>> - if (Solver.isMustTailCallee(&F)) {
>>> - LLVM_DEBUG(dbgs() << "Can't zap returns of the function : " <<
>>> F.getName()
>>> - << " due to present musttail call of it\n");
>>> + if (Solver.mustPreserveReturn(&F)) {
>>> + LLVM_DEBUG(
>>> + dbgs()
>>> + << "Can't zap returns of the function : " << F.getName()
>>> + << " due to present musttail or \"clang.arc.attachedcall\" call
>>> of "
>>> + "it\n");
>>> return;
>>> }
>>>
>>>
>>> diff --git a/llvm/lib/Transforms/Scalar/TailRecursionElimination.cpp
>>> b/llvm/lib/Transforms/Scalar/TailRecursionElimination.cpp
>>> index 9e7cccc884127..8cc649a8c1ed8 100644
>>> --- a/llvm/lib/Transforms/Scalar/TailRecursionElimination.cpp
>>> +++ b/llvm/lib/Transforms/Scalar/TailRecursionElimination.cpp
>>> @@ -247,7 +247,10 @@ static bool markTails(Function &F, bool
>>> &AllCallsAreTailCalls,
>>> isa<PseudoProbeInst>(&I))
>>> continue;
>>>
>>> - bool IsNoTail = CI->isNoTailCall() || CI->hasOperandBundles();
>>> + // Special-case operand bundle "clang.arc.attachedcall".
>>> + bool IsNoTail =
>>> + CI->isNoTailCall() || CI->hasOperandBundlesOtherThan(
>>> +
>>> LLVMContext::OB_clang_arc_attachedcall);
>>>
>>> if (!IsNoTail && CI->doesNotAccessMemory()) {
>>> // A call to a readnone function whose arguments are all things
>>> computed
>>>
>>> diff --git a/llvm/lib/Transforms/Utils/InlineFunction.cpp
>>> b/llvm/lib/Transforms/Utils/InlineFunction.cpp
>>> index 3026342cc4a6b..5f75ead1247bb 100644
>>> --- a/llvm/lib/Transforms/Utils/InlineFunction.cpp
>>> +++ b/llvm/lib/Transforms/Utils/InlineFunction.cpp
>>> @@ -27,8 +27,9 @@
>>> #include "llvm/Analysis/CaptureTracking.h"
>>> #include "llvm/Analysis/EHPersonalities.h"
>>> #include "llvm/Analysis/InstructionSimplify.h"
>>> +#include "llvm/Analysis/ObjCARCAnalysisUtils.h"
>>> +#include "llvm/Analysis/ObjCARCUtil.h"
>>> #include "llvm/Analysis/ProfileSummaryInfo.h"
>>> -#include "llvm/Transforms/Utils/Local.h"
>>> #include "llvm/Analysis/ValueTracking.h"
>>> #include "llvm/Analysis/VectorUtils.h"
>>> #include "llvm/IR/Argument.h"
>>> @@ -61,6 +62,7 @@
>>> #include "llvm/Support/ErrorHandling.h"
>>> #include "llvm/Transforms/Utils/AssumeBundleBuilder.h"
>>> #include "llvm/Transforms/Utils/Cloning.h"
>>> +#include "llvm/Transforms/Utils/Local.h"
>>> #include "llvm/Transforms/Utils/ValueMapper.h"
>>> #include <algorithm>
>>> #include <cassert>
>>> @@ -1650,6 +1652,99 @@ void llvm::updateProfileCallee(
>>> }
>>> }
>>>
>>> +/// An operand bundle "clang.arc.attachedcall" on a call indicates the
>>> call
>>> +/// result is implicitly consumed by a call to retainRV or claimRV
>>> immediately
>>> +/// after the call. This function inlines the retainRV/claimRV calls.
>>> +///
>>> +/// There are three cases to consider:
>>> +///
>>> +/// 1. If there is a call to autoreleaseRV that takes a pointer to the
>>> returned
>>> +/// object in the callee return block, the autoreleaseRV call and the
>>> +/// retainRV/claimRV call in the caller cancel out. If the call in
>>> the caller
>>> +/// is a claimRV call, a call to objc_release is emitted.
>>> +///
>>> +/// 2. If there is a call in the callee return block that doesn't have
>>> operand
>>> +/// bundle "clang.arc.attachedcall", the operand bundle on the
>>> original call
>>> +/// is transferred to the call in the callee.
>>> +///
>>> +/// 3. Otherwise, a call to objc_retain is inserted if the call in the
>>> caller is
>>> +/// a retainRV call.
>>> +static void
>>> +inlineRetainOrClaimRVCalls(CallBase &CB,
>>> + const SmallVectorImpl<ReturnInst *>
>>> &Returns) {
>>> + Module *Mod = CB.getModule();
>>> + bool IsRetainRV = objcarc::hasAttachedCallOpBundle(&CB, true),
>>> + IsClaimRV = !IsRetainRV;
>>> +
>>> + for (auto *RI : Returns) {
>>> + Value *RetOpnd = objcarc::GetRCIdentityRoot(RI->getOperand(0));
>>> + BasicBlock::reverse_iterator I = ++(RI->getIterator().getReverse());
>>> + BasicBlock::reverse_iterator EI = RI->getParent()->rend();
>>> + bool InsertRetainCall = IsRetainRV;
>>> + IRBuilder<> Builder(RI->getContext());
>>> +
>>> + // Walk backwards through the basic block looking for either a
>>> matching
>>> + // autoreleaseRV call or an unannotated call.
>>> + for (; I != EI;) {
>>> + auto CurI = I++;
>>> +
>>> + // Ignore casts.
>>> + if (isa<CastInst>(*CurI))
>>> + continue;
>>> +
>>> + if (auto *II = dyn_cast<IntrinsicInst>(&*CurI)) {
>>> + if (II->getIntrinsicID() ==
>>> Intrinsic::objc_autoreleaseReturnValue &&
>>> + II->hasNUses(0) &&
>>> + objcarc::GetRCIdentityRoot(II->getOperand(0)) == RetOpnd) {
>>> + // If we've found a matching authoreleaseRV call:
>>> + // - If claimRV is attached to the call, insert a call to
>>> objc_release
>>> + // and erase the autoreleaseRV call.
>>> + // - If retainRV is attached to the call, just erase the
>>> autoreleaseRV
>>> + // call.
>>> + if (IsClaimRV) {
>>> + Builder.SetInsertPoint(II);
>>> + Function *IFn =
>>> + Intrinsic::getDeclaration(Mod, Intrinsic::objc_release);
>>> + Value *BC =
>>> + Builder.CreateBitCast(RetOpnd,
>>> IFn->getArg(0)->getType());
>>> + Builder.CreateCall(IFn, BC, "");
>>> + }
>>> + II->eraseFromParent();
>>> + InsertRetainCall = false;
>>> + }
>>> + } else if (auto *CI = dyn_cast<CallInst>(&*CurI)) {
>>> + if (objcarc::GetRCIdentityRoot(CI) == RetOpnd &&
>>> + !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))};
>>> + OperandBundleDef OB("clang.arc.attachedcall", BundleArgs);
>>> + auto *NewCall = CallBase::addOperandBundle(
>>> + CI, LLVMContext::OB_clang_arc_attachedcall, OB, CI);
>>> + NewCall->copyMetadata(*CI);
>>> + CI->replaceAllUsesWith(NewCall);
>>> + CI->eraseFromParent();
>>> + InsertRetainCall = false;
>>> + }
>>> + }
>>> +
>>> + break;
>>> + }
>>> +
>>> + if (InsertRetainCall) {
>>> + // The retainRV is attached to the call and we've failed to find a
>>> + // matching autoreleaseRV or an annotated call in the callee.
>>> Emit a call
>>> + // to objc_retain.
>>> + Builder.SetInsertPoint(RI);
>>> + Function *IFn = Intrinsic::getDeclaration(Mod,
>>> Intrinsic::objc_retain);
>>> + Value *BC = Builder.CreateBitCast(RetOpnd,
>>> IFn->getArg(0)->getType());
>>> + Builder.CreateCall(IFn, BC, "");
>>> + }
>>> + }
>>> +}
>>> +
>>> /// This function inlines the called function into the basic block of
>>> the
>>> /// caller. This returns false if it is not possible to inline this
>>> call.
>>> /// The program is still in a well defined state if this occurs though.
>>> @@ -1687,6 +1782,8 @@ llvm::InlineResult llvm::InlineFunction(CallBase
>>> &CB, InlineFunctionInfo &IFI,
>>> // ... and "funclet" operand bundles.
>>> if (Tag == LLVMContext::OB_funclet)
>>> continue;
>>> + if (Tag == LLVMContext::OB_clang_arc_attachedcall)
>>> + continue;
>>>
>>> return InlineResult::failure("unsupported operand bundle");
>>> }
>>> @@ -1853,6 +1950,10 @@ llvm::InlineResult llvm::InlineFunction(CallBase
>>> &CB, InlineFunctionInfo &IFI,
>>> // Remember the first block that is newly cloned over.
>>> FirstNewBlock = LastBlock; ++FirstNewBlock;
>>>
>>> + // Insert retainRV/clainRV runtime calls.
>>> + if (objcarc::hasAttachedCallOpBundle(&CB))
>>> + inlineRetainOrClaimRVCalls(CB, Returns);
>>> +
>>> if (IFI.CallerBFI != nullptr && IFI.CalleeBFI != nullptr)
>>> // Update the BFI of blocks cloned into the caller.
>>> updateCallerBFI(OrigBB, VMap, IFI.CallerBFI, IFI.CalleeBFI,
>>>
>>> diff --git a/llvm/test/Bitcode/operand-bundles-bc-analyzer.ll
>>> b/llvm/test/Bitcode/operand-bundles-bc-analyzer.ll
>>> index f22fcbeb271ed..cb7a3ee7eb714 100644
>>> --- a/llvm/test/Bitcode/operand-bundles-bc-analyzer.ll
>>> +++ b/llvm/test/Bitcode/operand-bundles-bc-analyzer.ll
>>> @@ -9,6 +9,7 @@
>>> ; CHECK-NEXT: <OPERAND_BUNDLE_TAG
>>> ; CHECK-NEXT: <OPERAND_BUNDLE_TAG
>>> ; CHECK-NEXT: <OPERAND_BUNDLE_TAG
>>> +; CHECK-NEXT: <OPERAND_BUNDLE_TAG
>>> ; CHECK-NEXT: </OPERAND_BUNDLE_TAGS_BLOCK
>>>
>>> ; CHECK: <FUNCTION_BLOCK
>>>
>>> diff --git a/llvm/test/CodeGen/AArch64/call-rv-marker.ll
>>> b/llvm/test/CodeGen/AArch64/call-rv-marker.ll
>>> index 245d2c854c342..3b5ab40839b57 100644
>>> --- a/llvm/test/CodeGen/AArch64/call-rv-marker.ll
>>> +++ b/llvm/test/CodeGen/AArch64/call-rv-marker.ll
>>> @@ -33,7 +33,7 @@ define dso_local i8* @rv_marker_1() {
>>> ; GISEL-NOT: mov x29, x29
>>> ;
>>> entry:
>>> - %call = call "rv_marker" i8* @foo1()
>>> + %call = call i8* @foo1() [ "clang.arc.attachedcall"(i64 0) ]
>>> ret i8* %call
>>> }
>>>
>>> @@ -49,7 +49,7 @@ define dso_local void @rv_marker_2_select(i32 %c) {
>>> entry:
>>> %tobool.not = icmp eq i32 %c, 0
>>> %.sink = select i1 %tobool.not, i32 2, i32 1
>>> - %call1 = call "rv_marker" i8* @foo0(i32 %.sink)
>>> + %call1 = call i8* @foo0(i32 %.sink) [ "clang.arc.attachedcall"(i64 0)
>>> ]
>>> tail call void @foo2(i8* %call1)
>>> ret void
>>> }
>>> @@ -61,7 +61,7 @@ define dso_local void @rv_marker_3() personality i8*
>>> bitcast (i32 (...)* @__gxx_
>>> ; SELDAG-NEXT: mov x29, x29
>>> ;
>>> entry:
>>> - %call = call "rv_marker" i8* @foo1()
>>> + %call = call i8* @foo1() [ "clang.arc.attachedcall"(i64 0) ]
>>> invoke void @objc_object(i8* %call) #5
>>> to label %invoke.cont unwind label %lpad
>>>
>>> @@ -87,7 +87,7 @@ entry:
>>> %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 "rv_marker" i8* @foo1()
>>> + %call = invoke i8* @foo1() [ "clang.arc.attachedcall"(i64 0) ]
>>> to label %invoke.cont unwind label %lpad
>>>
>>> invoke.cont: ; preds = %entry
>>> @@ -127,12 +127,12 @@ define dso_local i8* @rv_marker_5_indirect_call() {
>>> ;
>>> entry:
>>> %0 = load i8* ()*, i8* ()** @fptr, align 8
>>> - %call = call "rv_marker" i8* %0()
>>> + %call = call i8* %0() [ "clang.arc.attachedcall"(i64 0) ]
>>> tail call void @foo2(i8* %call)
>>> ret i8* %call
>>> }
>>>
>>> -declare void @foo(i64, i64, i64)
>>> +declare i8* @foo(i64, i64, i64)
>>>
>>> define dso_local void @rv_marker_multiarg(i64 %a, i64 %b, i64 %c) {
>>> ; CHECK-LABEL: rv_marker_multiarg
>>> @@ -142,7 +142,7 @@ define dso_local void @rv_marker_multiarg(i64 %a,
>>> i64 %b, i64 %c) {
>>> ; CHECK-NEXT: bl foo
>>> ; SELDAG-NEXT: mov x29, x29
>>> ; GISEL-NOT: mov x29, x29
>>> - call "rv_marker" void @foo(i64 %c, i64 %b, i64 %a)
>>> + call i8* @foo(i64 %c, i64 %b, i64 %a) [ "clang.arc.attachedcall"(i64
>>> 0) ]
>>> ret void
>>> }
>>>
>>>
>>> diff --git a/llvm/test/Transforms/DeadArgElim/deadretval.ll
>>> b/llvm/test/Transforms/DeadArgElim/deadretval.ll
>>> index 5f3817c6728dd..f3b343745e4d2 100644
>>> --- a/llvm/test/Transforms/DeadArgElim/deadretval.ll
>>> +++ b/llvm/test/Transforms/DeadArgElim/deadretval.ll
>>> @@ -1,4 +1,8 @@
>>> -; RUN: opt < %s -deadargelim -S | not grep DEAD
>>> +; RUN: opt < %s -deadargelim -S | FileCheck %s
>>> +
>>> + at g0 = global i8 0, align 8
>>> +
>>> +; CHECK-NOT: DEAD
>>>
>>> ; Dead arg only used by dead retval
>>> define internal i32 @test(i32 %DEADARG) {
>>> @@ -16,3 +20,22 @@ define i32 @test3() {
>>> ret i32 %Y
>>> }
>>>
>>> +; The callee function's return type shouldn't be changed if the call
>>> result is
>>> +; used.
>>> +
>>> +; CHECK-LABEL: define internal i8* @callee4()
>>> +
>>> +define internal i8* @callee4(i8* %a0) {
>>> + ret i8* @g0;
>>> +}
>>> +
>>> +declare void @llvm.objc.clang.arc.noop.use(...)
>>> +
>>> +; CHECK-LABEL: define i8* @test4(
>>> +; CHECK: tail call i8* @callee4() [ "clang.arc.attachedcall"(i64 0) ]
>>> +
>>> +define i8* @test4() {
>>> + %call = tail call i8* @callee4(i8* @g0) [
>>> "clang.arc.attachedcall"(i64 0) ]
>>> + call void (...) @llvm.objc.clang.arc.noop.use(i8* %call)
>>> + ret i8* @g0
>>> +}
>>>
>>> diff --git a/llvm/test/Transforms/Inline/inline-retainRV-call.ll
>>> b/llvm/test/Transforms/Inline/inline-retainRV-call.ll
>>> new file mode 100644
>>> index 0000000000000..f8ac2154f9339
>>> --- /dev/null
>>> +++ b/llvm/test/Transforms/Inline/inline-retainRV-call.ll
>>> @@ -0,0 +1,175 @@
>>> +; RUN: opt < %s -inline -S | FileCheck %s
>>> +
>>> + at g0 = global i8* null, align 8
>>> +declare i8* @foo0()
>>> +
>>> +define i8* @callee0_autoreleaseRV() {
>>> + %call = call i8* @foo0() [ "clang.arc.attachedcall"(i64 0) ]
>>> + %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) ]
>>> +
>>> +define void @test0_autoreleaseRV() {
>>> + %call = call i8* @callee0_autoreleaseRV() [
>>> "clang.arc.attachedcall"(i64 0) ]
>>> + ret void
>>> +}
>>> +
>>> +; CHECK-LABEL: define void @test0_claimRV_autoreleaseRV(
>>> +; CHECK: %[[CALL:.*]] = call i8* @foo0() [ "clang.arc.attachedcall"(i64
>>> 0) ]
>>> +; 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) ]
>>> + ret void
>>> +}
>>> +
>>> +; CHECK-LABEL: define void @test1_autoreleaseRV(
>>> +; CHECK: invoke i8* @foo0() [ "clang.arc.attachedcall"(i64 0) ]
>>> +
>>> +define void @test1_autoreleaseRV() personality i8* bitcast (i32 (...)*
>>> @__gxx_personality_v0 to i8*) {
>>> +entry:
>>> + %call = invoke i8* @callee0_autoreleaseRV() [
>>> "clang.arc.attachedcall"(i64 0) ]
>>> + to label %invoke.cont unwind label %lpad
>>> +
>>> +invoke.cont:
>>> + ret void
>>> +
>>> +lpad:
>>> + %0 = landingpad { i8*, i32 }
>>> + cleanup
>>> + resume { i8*, i32 } undef
>>> +}
>>> +
>>> +; CHECK-LABEL: define void @test1_claimRV_autoreleaseRV(
>>> +; CHECK: %[[INVOKE:.*]] = invoke i8* @foo0() [
>>> "clang.arc.attachedcall"(i64 0) ]
>>> +; 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) ]
>>> + to label %invoke.cont unwind label %lpad
>>> +
>>> +invoke.cont:
>>> + ret void
>>> +
>>> +lpad:
>>> + %0 = landingpad { i8*, i32 }
>>> + cleanup
>>> + resume { i8*, i32 } undef
>>> +}
>>> +
>>> +define i8* @callee1_no_autoreleaseRV() {
>>> + %call = call i8* @foo0()
>>> + ret i8* %call
>>> +}
>>> +
>>> +; CHECK-LABEL: define void @test2_no_autoreleaseRV(
>>> +; CHECK: call i8* @foo0() [ "clang.arc.attachedcall"(i64 0) ]
>>> +; CHECK-NEXT: ret void
>>> +
>>> +define void @test2_no_autoreleaseRV() {
>>> + %call = call i8* @callee1_no_autoreleaseRV() [
>>> "clang.arc.attachedcall"(i64 0) ]
>>> + ret void
>>> +}
>>> +
>>> +; CHECK-LABEL: define void @test2_claimRV_no_autoreleaseRV(
>>> +; CHECK: call i8* @foo0() [ "clang.arc.attachedcall"(i64 1) ]
>>> +; CHECK-NEXT: ret void
>>> +
>>> +define void @test2_claimRV_no_autoreleaseRV() {
>>> + %call = call i8* @callee1_no_autoreleaseRV() [
>>> "clang.arc.attachedcall"(i64 1) ]
>>> + ret void
>>> +}
>>> +
>>> +; CHECK-LABEL: define void @test3_no_autoreleaseRV(
>>> +; CHECK: invoke i8* @foo0() [ "clang.arc.attachedcall"(i64 0) ]
>>> +
>>> +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) ]
>>> + to label %invoke.cont unwind label %lpad
>>> +
>>> +invoke.cont:
>>> + ret void
>>> +
>>> +lpad:
>>> + %0 = landingpad { i8*, i32 }
>>> + cleanup
>>> + resume { i8*, i32 } undef
>>> +}
>>> +
>>> +define i8* @callee2_nocall() {
>>> + %1 = load i8*, i8** @g0, align 8
>>> + ret i8* %1
>>> +}
>>> +
>>> +; Check that a call to @llvm.objc.retain is inserted if there is no
>>> matching
>>> +; autoreleaseRV call or a call.
>>> +
>>> +; CHECK-LABEL: define void @test4_nocall(
>>> +; CHECK: %[[V0:.*]] = load i8*, i8** @g0,
>>> +; CHECK-NEXT: call i8* @llvm.objc.retain(i8* %[[V0]])
>>> +; CHECK-NEXT: ret void
>>> +
>>> +define void @test4_nocall() {
>>> + %call = call i8* @callee2_nocall() [ "clang.arc.attachedcall"(i64 0) ]
>>> + ret void
>>> +}
>>> +
>>> +; CHECK-LABEL: define void @test4_claimRV_nocall(
>>> +; CHECK: %[[V0:.*]] = load i8*, i8** @g0,
>>> +; CHECK-NEXT: ret void
>>> +
>>> +define void @test4_claimRV_nocall() {
>>> + %call = call i8* @callee2_nocall() [ "clang.arc.attachedcall"(i64 1) ]
>>> + ret void
>>> +}
>>> +
>>> +; Check that a call to @llvm.objc.retain is inserted if call to @foo
>>> already has
>>> +; the attribute. I'm not sure this will happen in practice.
>>> +
>>> +define i8* @callee3_marker() {
>>> + %1 = call i8* @foo0() [ "clang.arc.attachedcall"(i64 0) ]
>>> + ret i8* %1
>>> +}
>>> +
>>> +; CHECK-LABEL: define void @test5(
>>> +; CHECK: %[[V0:.*]] = call i8* @foo0() [ "clang.arc.attachedcall"(i64
>>> 0) ]
>>> +; 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) ]
>>> + ret void
>>> +}
>>> +
>>> +; Don't pair up an autoreleaseRV in the callee and an retainRV in the
>>> caller
>>> +; if there is an instruction between the ret instruction and the call to
>>> +; autoreleaseRV that isn't a cast instruction.
>>> +
>>> +define i8* @callee0_autoreleaseRV2() {
>>> + %call = call i8* @foo0() [ "clang.arc.attachedcall"(i64 0) ]
>>> + %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: 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) ]
>>> + ret void
>>> +}
>>> +
>>> +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
>>> index c0d6e0033dda2..c40f411c9fb75 100644
>>> --- a/llvm/test/Transforms/ObjCARC/contract-marker-funclet.ll
>>> +++ b/llvm/test/Transforms/ObjCARC/contract-marker-funclet.ll
>>> @@ -10,6 +10,16 @@
>>> ; }
>>> ; }
>>>
>>> +; CHECK-LABEL: define void @"\01?g@@YAXXZ"()
>>> +; CHECK-LABEL: catch
>>> +; CHECK: call void asm sideeffect "movl{{.*}}%ebp, %ebp{{.*}}", ""() [
>>> "funclet"(token %1) ]
>>> +
>>> +; CHECK-LABEL: catch.1
>>> +; CHECK: call void asm sideeffect "movl{{.*}}%ebp, %ebp{{.*}}", ""() [
>>> "funclet"(token %1) ]
>>> +
>>> +; CHECK-LABEL: invoke.cont
>>> +; CHECK: call void asm sideeffect "movl{{.*}}%ebp, %ebp{{.*}}",
>>> ""(){{$}}
>>> +
>>> define void @"\01?g@@YAXXZ"() personality i8* bitcast (i32 (...)*
>>> @__CxxFrameHandler3 to i8*) {
>>> entry:
>>> %call = invoke i8* @"\01?f@@YAPAUobjc_object@@XZ"()
>>> @@ -40,23 +50,41 @@ invoke.cont: ;
>>> preds = %entry
>>> ret void
>>> }
>>>
>>> +; 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: 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 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*) {
>>> +entry:
>>> + invoke void @"?foo@@YAXXZ"()
>>> + 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) ]
>>> + 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) ]
>>> + cleanupret from %0 unwind to caller
>>> +}
>>> +
>>> declare i8* @"\01?f@@YAPAUobjc_object@@XZ"()
>>>
>>> declare i32 @__CxxFrameHandler3(...)
>>>
>>> +declare void @"?foo@@YAXXZ"()
>>> +declare i8* @"?noexcept_func@@YAPAUobjc_object@@XZ"()
>>> +
>>> declare dllimport i8* @llvm.objc.retainAutoreleasedReturnValue(i8*)
>>> +declare i8* @llvm.objc.unsafeClaimAutoreleasedReturnValue(i8*)
>>>
>>> declare dllimport void @llvm.objc.release(i8*)
>>>
>>> !llvm.module.flags = !{!0}
>>>
>>> !0 = !{i32 1, !"clang.arc.retainAutoreleasedReturnValueMarker",
>>> !"movl\09%ebp, %ebp\09\09// marker for objc_retainAutoreleaseReturnValue"}
>>> -
>>> -; CHECK-LABEL: catch
>>> -; CHECK: call void asm sideeffect "movl{{.*}}%ebp, %ebp{{.*}}", ""() [
>>> "funclet"(token %1) ]
>>> -
>>> -; CHECK-LABEL: catch.1
>>> -; CHECK: call void asm sideeffect "movl{{.*}}%ebp, %ebp{{.*}}", ""() [
>>> "funclet"(token %1) ]
>>> -
>>> -; CHECK-LABEL: invoke.cont
>>> -; CHECK: call void asm sideeffect "movl{{.*}}%ebp, %ebp{{.*}}",
>>> ""(){{$}}
>>>
>>> diff --git a/llvm/test/Transforms/ObjCARC/contract-rv-attr.ll
>>> b/llvm/test/Transforms/ObjCARC/contract-rv-attr.ll
>>> new file mode 100644
>>> index 0000000000000..3a817327c3638
>>> --- /dev/null
>>> +++ b/llvm/test/Transforms/ObjCARC/contract-rv-attr.ll
>>> @@ -0,0 +1,63 @@
>>> +; RUN: opt -objc-arc-contract -S < %s | FileCheck %s
>>> +; 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 i8* @llvm.objc.retainAutoreleasedReturnValue(i8*
>>> %[[CALL]])
>>> +
>>> +define void @test0() {
>>> + %call1 = call i8* @foo() [ "clang.arc.attachedcall"(i64 0) ]
>>> + ret void
>>> +}
>>> +
>>> +; CHECK-LABEL: define void @test1() {
>>> +; CHECK: %[[CALL:.*]] = notail call i8* @foo() [
>>> "clang.arc.attachedcall"(i64 1) ]
>>> +; CHECK: call i8* @llvm.objc.unsafeClaimAutoreleasedReturnValue(i8*
>>> %[[CALL]])
>>> +
>>> +define void @test1() {
>>> + %call1 = call i8* @foo() [ "clang.arc.attachedcall"(i64 1) ]
>>> + ret void
>>> +}
>>> +
>>> +; CHECK-LABEL:define i8* @test2(
>>> +; CHECK: %[[CALL1:.*]] = invoke i8* @foo() [
>>> "clang.arc.attachedcall"(i64 0) ]
>>> +
>>> +; CHECK: %[[V0:.*]] = call i8*
>>> @llvm.objc.retainAutoreleasedReturnValue(i8* %[[CALL1]])
>>> +; CHECK-NEXT: br
>>> +
>>> +; CHECK: %[[CALL3:.*]] = invoke i8* @foo() [
>>> "clang.arc.attachedcall"(i64 0) ]
>>> +
>>> +; CHECK: %[[V2:.*]] = call i8*
>>> @llvm.objc.retainAutoreleasedReturnValue(i8* %[[CALL3]])
>>> +; CHECK-NEXT: br
>>> +
>>> +; CHECK: %[[RETVAL:.*]] = phi i8* [ %[[V0]], {{.*}} ], [ %[[V2]],
>>> {{.*}} ]
>>> +; CHECK: ret i8* %[[RETVAL]]
>>> +
>>> +define i8* @test2(i1 zeroext %b) personality i8* bitcast (i32 (...)*
>>> @__gxx_personality_v0 to i8*) {
>>> +entry:
>>> + br i1 %b, label %if.then, label %if.end
>>> +
>>> +if.then:
>>> + %call1 = invoke i8* @foo() [ "clang.arc.attachedcall"(i64 0) ]
>>> + to label %cleanup unwind label %lpad
>>> +
>>> +lpad:
>>> + %0 = landingpad { i8*, i32 }
>>> + cleanup
>>> + resume { i8*, i32 } undef
>>> +
>>> +if.end:
>>> + %call3 = invoke i8* @foo() [ "clang.arc.attachedcall"(i64 0) ]
>>> + to label %cleanup unwind label %lpad
>>> +
>>> +cleanup:
>>> + %retval.0 = phi i8* [ %call1, %if.then ], [ %call3, %if.end ]
>>> + ret i8* %retval.0
>>> +}
>>> +
>>> +declare i8* @foo()
>>> +declare i32 @__gxx_personality_v0(...)
>>> +
>>> +!llvm.module.flags = !{!0}
>>> +
>>> +!0 = !{i32 1, !"clang.arc.retainAutoreleasedReturnValueMarker",
>>> !"mov\09fp, fp\09\09// marker for objc_retainAutoreleaseReturnValue"}
>>>
>>> diff --git a/llvm/test/Transforms/ObjCARC/contract.ll
>>> b/llvm/test/Transforms/ObjCARC/contract.ll
>>> index d62fe221529e2..36f0a842d0e66 100644
>>> --- a/llvm/test/Transforms/ObjCARC/contract.ll
>>> +++ b/llvm/test/Transforms/ObjCARC/contract.ll
>>> @@ -227,7 +227,15 @@ define void @test13() {
>>> ret void
>>> }
>>>
>>> +; CHECK-LABEL: define void @test14(
>>> +; CHECK-NOT: clang.arc.noop.use
>>> +; CHECK: ret void
>>> +define void @test14(i8* %a, i8* %b) {
>>> + call void (...) @llvm.objc.clang.arc.noop.use(i8* %a, i8* %b) nounwind
>>> + ret void
>>> +}
>>>
>>> declare void @llvm.objc.clang.arc.use(...) nounwind
>>> +declare void @llvm.objc.clang.arc.noop.use(...) nounwind
>>>
>>> ; CHECK: attributes [[NUW]] = { nounwind }
>>>
>>> diff --git a/llvm/test/Transforms/ObjCARC/intrinsic-use.ll
>>> b/llvm/test/Transforms/ObjCARC/intrinsic-use.ll
>>> index 6f3815113d698..bd4ac2965fb34 100644
>>> --- a/llvm/test/Transforms/ObjCARC/intrinsic-use.ll
>>> +++ b/llvm/test/Transforms/ObjCARC/intrinsic-use.ll
>>> @@ -8,8 +8,10 @@ declare void @llvm.objc.release(i8*)
>>> declare i8* @llvm.objc.autorelease(i8*)
>>>
>>> declare void @llvm.objc.clang.arc.use(...)
>>> +declare void @llvm.objc.clang.arc.noop.use(...)
>>>
>>> declare void @test0_helper(i8*, i8**)
>>> +declare void @can_release(i8*)
>>>
>>> ; Ensure that we honor clang.arc.use as a use and don't miscompile
>>> ; the reduced test case from <rdar://13195034>.
>>> @@ -108,6 +110,21 @@ entry:
>>> ret void
>>> }
>>>
>>> +; ARC optimizer should be able to safely remove the retain/release pair
>>> as the
>>> +; call to @llvm.objc.clang.arc.noop.use is a no-op.
>>> +
>>> +; CHECK-LABEL: define void @test_arc_noop_use(
>>> +; CHECK-NEXT: call void @can_release(i8* %x)
>>> +; CHECK-NEXT: call void (...) @llvm.objc.clang.arc.noop.use(
>>> +; CHECK-NEXT: ret void
>>> +
>>> +define void @test_arc_noop_use(i8** %out, i8* %x) {
>>> + call i8* @llvm.objc.retain(i8* %x)
>>> + call void @can_release(i8* %x)
>>> + call void (...) @llvm.objc.clang.arc.noop.use(i8* %x)
>>> + call void @llvm.objc.release(i8* %x), !clang.imprecise_release !0
>>> + ret void
>>> +}
>>>
>>> !0 = !{}
>>>
>>>
>>> diff --git a/llvm/test/Transforms/ObjCARC/rv.ll
>>> b/llvm/test/Transforms/ObjCARC/rv.ll
>>> index f89e9f7e39d99..29017222ebb11 100644
>>> --- a/llvm/test/Transforms/ObjCARC/rv.ll
>>> +++ b/llvm/test/Transforms/ObjCARC/rv.ll
>>> @@ -11,6 +11,7 @@ declare i8*
>>> @llvm.objc.retainAutoreleaseReturnValue(i8*)
>>> declare void @llvm.objc.autoreleasePoolPop(i8*)
>>> declare void @llvm.objc.autoreleasePoolPush()
>>> declare i8* @llvm.objc.retainBlock(i8*)
>>> +declare void @llvm.objc.clang.arc.noop.use(...)
>>>
>>> declare i8* @objc_retainedObject(i8*)
>>> declare i8* @objc_unretainedObject(i8*)
>>> @@ -452,6 +453,32 @@ bb1:
>>> ret i8* %v3
>>> }
>>>
>>> +; Remove operand bundle "clang.arc.attachedcall" and the autoreleaseRV
>>> call if the call
>>> +; is a tail call.
>>> +
>>> +; CHECK-LABEL: define i8* @test31(
>>> +; CHECK-NEXT: %[[CALL:.*]] = tail call i8* @returner()
>>> +; CHECK-NEXT: ret i8* %[[CALL]]
>>> +
>>> +define i8* @test31() {
>>> + %call = tail call i8* @returner() [ "clang.arc.attachedcall"(i64 0) ]
>>> + 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 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 void (...) @llvm.objc.clang.arc.noop.use(i8* %call)
>>> + %1 = call i8* @llvm.objc.autoreleaseReturnValue(i8* %call)
>>> + ret i8* %1
>>> +}
>>> +
>>> !0 = !{}
>>>
>>> ; CHECK: attributes [[NUW]] = { nounwind }
>>>
>>> diff --git a/llvm/test/Transforms/SCCP/clang-arc-rv.ll
>>> b/llvm/test/Transforms/SCCP/clang-arc-rv.ll
>>> new file mode 100644
>>> index 0000000000000..bb8f45b41c136
>>> --- /dev/null
>>> +++ b/llvm/test/Transforms/SCCP/clang-arc-rv.ll
>>> @@ -0,0 +1,24 @@
>>> +; RUN: opt < %s -ipsccp -S | FileCheck %s
>>> +; Return value can't be zapped if there is a call that has operand
>>> bundle
>>> +; "clang.arc.attachedcall".
>>> +
>>> + at g0 = global i8 zeroinitializer, align 1
>>> +
>>> +; CHECK-LABEL: @foo(
>>> +; CHECK: ret i8* @g0
>>> +
>>> +define internal i8* @foo() {
>>> + ret i8* @g0
>>> +}
>>> +
>>> +; CHECK-LABEL: @test(
>>> +; CHECK: %[[R:.*]] = call i8* @foo()
>>> +; CHECK call void (...) @llvm.objc.clang.arc.noop.use(i8* %[[R]])
>>> +
>>> +define void @test() {
>>> + %r = call i8* @foo() [ "clang.arc.attachedcall"(i64 1) ]
>>> + call void (...) @llvm.objc.clang.arc.noop.use(i8* %r)
>>> + ret void
>>> +}
>>> +
>>> +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
>>> index f651e462c1f56..a075849421716 100644
>>> --- a/llvm/test/Transforms/TailCallElim/deopt-bundle.ll
>>> +++ b/llvm/test/Transforms/TailCallElim/deopt-bundle.ll
>>> @@ -55,3 +55,13 @@ catch:
>>> exit:
>>> ret void
>>> }
>>> +
>>> +; CHECK-LABEL: @test_clang_arc_attachedcall(
>>> +; CHECK: tail call i8* @getObj(
>>> +
>>> +declare i8* @getObj()
>>> +
>>> +define i8* @test_clang_arc_attachedcall() {
>>> + %r = call i8* @getObj() [ "clang.arc.attachedcall"(i64 0) ]
>>> + ret i8* %r
>>> +}
>>>
>>> diff --git a/llvm/test/Verifier/operand-bundles.ll
>>> b/llvm/test/Verifier/operand-bundles.ll
>>> index 2598a7889f3fd..4ef0e647988af 100644
>>> --- a/llvm/test/Verifier/operand-bundles.ll
>>> +++ b/llvm/test/Verifier/operand-bundles.ll
>>> @@ -1,10 +1,13 @@
>>> ; RUN: not opt -verify < %s 2>&1 | FileCheck %s
>>>
>>> +%0 = type opaque
>>> +declare void @g()
>>> +declare %0* @foo0()
>>> +declare i8 @foo1()
>>> +
>>> ; Operand bundles uses are like regular uses, and need to be dominated
>>> ; by their defs.
>>>
>>> -declare void @g()
>>> -
>>> define void @f0(i32* %ptr) {
>>> ; CHECK: Instruction does not dominate all uses!
>>> ; CHECK-NEXT: %x = add i32 42, 1
>>> @@ -60,3 +63,15 @@ define void @f_gc_transition(i32* %ptr) {
>>> %x = add i32 42, 1
>>> ret void
>>> }
>>> +
>>> +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: must call a function returning a pointer
>>> +; CHECK-NEXT: call i8 @foo1() [ "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) ]
>>> + ret void
>>> +}
>>>
>>>
>>>
>>> _______________________________________________
>>> llvm-commits mailing list
>>> llvm-commits at lists.llvm.org
>>> https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits
>>>
>> _______________________________________________
>> llvm-commits mailing list
>> llvm-commits at lists.llvm.org
>> https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-commits/attachments/20210305/e938b229/attachment-0001.html>
More information about the llvm-commits
mailing list