diff options
author | John McCall <rjmccall@apple.com> | 2016-01-27 19:05:08 +0000 |
---|---|---|
committer | John McCall <rjmccall@apple.com> | 2016-01-27 19:05:08 +0000 |
commit | 3fe604f89fccfe1534eeaf4b4e328185d01659f6 (patch) | |
tree | a5113ad2639cc50d9a0a3f68f993f3d26d634ccf | |
parent | 671e6340da589fe94d30fa97fb3c89318881a775 (diff) | |
download | bcm5719-llvm-3fe604f89fccfe1534eeaf4b4e328185d01659f6.tar.gz bcm5719-llvm-3fe604f89fccfe1534eeaf4b4e328185d01659f6.zip |
Add support for objc_unsafeClaimAutoreleasedReturnValue to the
ObjC ARC Optimizer.
The main implication of this is:
1. Ensuring that we treat it conservatively in terms of optimization.
2. We put the ASM marker on it so that the runtime can recognize
objc_unsafeClaimAutoreleasedReturnValue from releaseRV.
<rdar://problem/21567064>
Patch by Michael Gottesman!
llvm-svn: 258970
-rw-r--r-- | llvm/include/llvm/Analysis/ObjCARCAnalysisUtils.h | 1 | ||||
-rw-r--r-- | llvm/include/llvm/Analysis/ObjCARCInstKind.h | 1 | ||||
-rw-r--r-- | llvm/lib/Analysis/ObjCARCInstKind.cpp | 14 | ||||
-rw-r--r-- | llvm/lib/Transforms/ObjCARC/ObjCARCContract.cpp | 32 | ||||
-rw-r--r-- | llvm/test/Transforms/ObjCARC/basic.ll | 22 | ||||
-rw-r--r-- | llvm/test/Transforms/ObjCARC/contract-marker.ll | 24 | ||||
-rw-r--r-- | llvm/test/Transforms/ObjCARC/tail-call-invariant-enforcement.ll | 15 |
7 files changed, 90 insertions, 19 deletions
diff --git a/llvm/include/llvm/Analysis/ObjCARCAnalysisUtils.h b/llvm/include/llvm/Analysis/ObjCARCAnalysisUtils.h index 29d99c9d316..5f4d8ecbbfb 100644 --- a/llvm/include/llvm/Analysis/ObjCARCAnalysisUtils.h +++ b/llvm/include/llvm/Analysis/ObjCARCAnalysisUtils.h @@ -54,6 +54,7 @@ inline bool ModuleHasARC(const Module &M) { M.getNamedValue("objc_release") || M.getNamedValue("objc_autorelease") || M.getNamedValue("objc_retainAutoreleasedReturnValue") || + M.getNamedValue("objc_unsafeClaimAutoreleasedReturnValue") || M.getNamedValue("objc_retainBlock") || M.getNamedValue("objc_autoreleaseReturnValue") || M.getNamedValue("objc_autoreleasePoolPush") || diff --git a/llvm/include/llvm/Analysis/ObjCARCInstKind.h b/llvm/include/llvm/Analysis/ObjCARCInstKind.h index 13efb4b160b..3b37ddf78f5 100644 --- a/llvm/include/llvm/Analysis/ObjCARCInstKind.h +++ b/llvm/include/llvm/Analysis/ObjCARCInstKind.h @@ -30,6 +30,7 @@ namespace objcarc { enum class ARCInstKind { Retain, ///< objc_retain RetainRV, ///< objc_retainAutoreleasedReturnValue + ClaimRV, ///< objc_unsafeClaimAutoreleasedReturnValue RetainBlock, ///< objc_retainBlock Release, ///< objc_release Autorelease, ///< objc_autorelease diff --git a/llvm/lib/Analysis/ObjCARCInstKind.cpp b/llvm/lib/Analysis/ObjCARCInstKind.cpp index 133b63513c8..3dc1463b8d8 100644 --- a/llvm/lib/Analysis/ObjCARCInstKind.cpp +++ b/llvm/lib/Analysis/ObjCARCInstKind.cpp @@ -34,6 +34,8 @@ raw_ostream &llvm::objcarc::operator<<(raw_ostream &OS, return OS << "ARCInstKind::Retain"; case ARCInstKind::RetainRV: return OS << "ARCInstKind::RetainRV"; + case ARCInstKind::ClaimRV: + return OS << "ARCInstKind::ClaimRV"; case ARCInstKind::RetainBlock: return OS << "ARCInstKind::RetainBlock"; case ARCInstKind::Release: @@ -103,6 +105,8 @@ ARCInstKind llvm::objcarc::GetFunctionClass(const Function *F) { return StringSwitch<ARCInstKind>(F->getName()) .Case("objc_retain", ARCInstKind::Retain) .Case("objc_retainAutoreleasedReturnValue", ARCInstKind::RetainRV) + .Case("objc_unsafeClaimAutoreleasedReturnValue", + ARCInstKind::ClaimRV) .Case("objc_retainBlock", ARCInstKind::RetainBlock) .Case("objc_release", ARCInstKind::Release) .Case("objc_autorelease", ARCInstKind::Autorelease) @@ -350,6 +354,7 @@ bool llvm::objcarc::IsUser(ARCInstKind Class) { case ARCInstKind::StoreStrong: case ARCInstKind::Call: case ARCInstKind::None: + case ARCInstKind::ClaimRV: return false; } llvm_unreachable("covered switch isn't covered?"); @@ -385,6 +390,7 @@ bool llvm::objcarc::IsRetain(ARCInstKind Class) { case ARCInstKind::Call: case ARCInstKind::User: case ARCInstKind::None: + case ARCInstKind::ClaimRV: return false; } llvm_unreachable("covered switch isn't covered?"); @@ -398,6 +404,7 @@ bool llvm::objcarc::IsAutorelease(ARCInstKind Class) { return true; case ARCInstKind::Retain: case ARCInstKind::RetainRV: + case ARCInstKind::ClaimRV: case ARCInstKind::RetainBlock: case ARCInstKind::Release: case ARCInstKind::AutoreleasepoolPush: @@ -429,6 +436,7 @@ bool llvm::objcarc::IsForwarding(ARCInstKind Class) { switch (Class) { case ARCInstKind::Retain: case ARCInstKind::RetainRV: + case ARCInstKind::ClaimRV: case ARCInstKind::Autorelease: case ARCInstKind::AutoreleaseRV: case ARCInstKind::NoopCast: @@ -463,6 +471,7 @@ bool llvm::objcarc::IsNoopOnNull(ARCInstKind Class) { switch (Class) { case ARCInstKind::Retain: case ARCInstKind::RetainRV: + case ARCInstKind::ClaimRV: case ARCInstKind::Release: case ARCInstKind::Autorelease: case ARCInstKind::AutoreleaseRV: @@ -498,6 +507,7 @@ bool llvm::objcarc::IsAlwaysTail(ARCInstKind Class) { switch (Class) { case ARCInstKind::Retain: case ARCInstKind::RetainRV: + case ARCInstKind::ClaimRV: case ARCInstKind::AutoreleaseRV: return true; case ARCInstKind::Release: @@ -538,6 +548,7 @@ bool llvm::objcarc::IsNeverTail(ARCInstKind Class) { return true; case ARCInstKind::Retain: case ARCInstKind::RetainRV: + case ARCInstKind::ClaimRV: case ARCInstKind::AutoreleaseRV: case ARCInstKind::Release: case ARCInstKind::RetainBlock: @@ -572,6 +583,7 @@ bool llvm::objcarc::IsNoThrow(ARCInstKind Class) { switch (Class) { case ARCInstKind::Retain: case ARCInstKind::RetainRV: + case ARCInstKind::ClaimRV: case ARCInstKind::Release: case ARCInstKind::Autorelease: case ARCInstKind::AutoreleaseRV: @@ -616,6 +628,7 @@ bool llvm::objcarc::CanInterruptRV(ARCInstKind Class) { return true; case ARCInstKind::Retain: case ARCInstKind::RetainRV: + case ARCInstKind::ClaimRV: case ARCInstKind::Release: case ARCInstKind::AutoreleasepoolPush: case ARCInstKind::RetainBlock: @@ -668,6 +681,7 @@ bool llvm::objcarc::CanDecrementRefCount(ARCInstKind Kind) { case ARCInstKind::StoreStrong: case ARCInstKind::CallOrUser: case ARCInstKind::Call: + case ARCInstKind::ClaimRV: return true; } diff --git a/llvm/lib/Transforms/ObjCARC/ObjCARCContract.cpp b/llvm/lib/Transforms/ObjCARC/ObjCARCContract.cpp index 1cdf5689f42..edef360be61 100644 --- a/llvm/lib/Transforms/ObjCARC/ObjCARCContract.cpp +++ b/llvm/lib/Transforms/ObjCARC/ObjCARCContract.cpp @@ -66,7 +66,7 @@ namespace { /// The inline asm string to insert between calls and RetainRV calls to make /// the optimization work on targets which need it. - const MDString *RetainRVMarker; + const MDString *RVInstMarker; /// The set of inserted objc_storeStrong calls. If at the end of walking the /// function we have found no alloca instructions, these calls can be marked @@ -423,16 +423,16 @@ bool ObjCARCContract::tryToPeepholeInstruction( return false; // If we succeed in our optimization, fall through. // FALLTHROUGH - case ARCInstKind::RetainRV: { + case ARCInstKind::RetainRV: + case ARCInstKind::ClaimRV: { // If we're compiling for a target which needs a special inline-asm - // marker to do the retainAutoreleasedReturnValue optimization, - // insert it now. - if (!RetainRVMarker) + // marker to do the return value optimization, insert it now. + if (!RVInstMarker) return false; BasicBlock::iterator BBI = Inst->getIterator(); BasicBlock *InstParent = Inst->getParent(); - // Step up to see if the call immediately precedes the RetainRV call. + // Step up to see if the call immediately precedes the RV call. // If it's an invoke, we have to cross a block boundary. And we have // to carefully dodge no-op instructions. do { @@ -447,14 +447,14 @@ bool ObjCARCContract::tryToPeepholeInstruction( } while (IsNoopInstruction(&*BBI)); if (&*BBI == GetArgRCIdentityRoot(Inst)) { - DEBUG(dbgs() << "Adding inline asm marker for " - "retainAutoreleasedReturnValue optimization.\n"); + DEBUG(dbgs() << "Adding inline asm marker for the return value " + "optimization.\n"); Changed = true; - InlineAsm *IA = - InlineAsm::get(FunctionType::get(Type::getVoidTy(Inst->getContext()), - /*isVarArg=*/false), - RetainRVMarker->getString(), - /*Constraints=*/"", /*hasSideEffects=*/true); + InlineAsm *IA = InlineAsm::get( + FunctionType::get(Type::getVoidTy(Inst->getContext()), + /*isVarArg=*/false), + RVInstMarker->getString(), + /*Constraints=*/"", /*hasSideEffects=*/true); CallInst::Create(IA, "", Inst); } decline_rv_optimization: @@ -650,15 +650,15 @@ bool ObjCARCContract::doInitialization(Module &M) { EP.init(&M); - // Initialize RetainRVMarker. - RetainRVMarker = nullptr; + // Initialize RVInstMarker. + RVInstMarker = nullptr; if (NamedMDNode *NMD = M.getNamedMetadata("clang.arc.retainAutoreleasedReturnValueMarker")) if (NMD->getNumOperands() == 1) { const MDNode *N = NMD->getOperand(0); if (N->getNumOperands() == 1) if (const MDString *S = dyn_cast<MDString>(N->getOperand(0))) - RetainRVMarker = S; + RVInstMarker = S; } return false; diff --git a/llvm/test/Transforms/ObjCARC/basic.ll b/llvm/test/Transforms/ObjCARC/basic.ll index fc1d087794d..a6fdf5efa1a 100644 --- a/llvm/test/Transforms/ObjCARC/basic.ll +++ b/llvm/test/Transforms/ObjCARC/basic.ll @@ -4,6 +4,7 @@ target datalayout = "e-p:64:64:64" declare i8* @objc_retain(i8*) declare i8* @objc_retainAutoreleasedReturnValue(i8*) +declare i8* @objc_unsafeClaimAutoreleasedReturnValue(i8*) declare void @objc_release(i8*) declare i8* @objc_autorelease(i8*) declare i8* @objc_autoreleaseReturnValue(i8*) @@ -2573,6 +2574,27 @@ return: ; preds = %if.then, %entry ret i8* %retval } +; CHECK-LABEL: define i8* @test65d( +; CHECK: if.then: +; CHECK-NOT: @objc_autorelease +; CHECK: return: +; CHECK: call i8* @objc_autoreleaseReturnValue( +; CHECK: } +define i8* @test65d(i1 %x) { +entry: + br i1 %x, label %return, label %if.then + +if.then: ; preds = %entry + %c = call i8* @returner() + %s = call i8* @objc_unsafeClaimAutoreleasedReturnValue(i8* %c) nounwind + br label %return + +return: ; preds = %if.then, %entry + %retval = phi i8* [ %s, %if.then ], [ null, %entry ] + %q = call i8* @objc_autoreleaseReturnValue(i8* %retval) nounwind + ret i8* %retval +} + ; An objc_retain can serve as a may-use for a different pointer. ; rdar://11931823 diff --git a/llvm/test/Transforms/ObjCARC/contract-marker.ll b/llvm/test/Transforms/ObjCARC/contract-marker.ll index a8282607cb3..bf70d4e9d04 100644 --- a/llvm/test/Transforms/ObjCARC/contract-marker.ll +++ b/llvm/test/Transforms/ObjCARC/contract-marker.ll @@ -1,9 +1,9 @@ ; RUN: opt -S -objc-arc-contract < %s | FileCheck %s -; CHECK: define void @foo() { +; CHECK-LABEL: define void @foo() { ; CHECK: %call = tail call i32* @qux() ; CHECK-NEXT: %tcall = bitcast i32* %call to i8* -; CHECK-NEXT: call void asm sideeffect "mov\09r7, r7\09\09@ marker for objc_retainAutoreleaseReturnValue", ""() +; CHECK-NEXT: call void asm sideeffect "mov\09r7, r7\09\09@ marker for return value optimization", ""() ; CHECK-NEXT: %0 = tail call i8* @objc_retainAutoreleasedReturnValue(i8* %tcall) [[NUW:#[0-9]+]] ; CHECK: } @@ -16,12 +16,30 @@ entry: ret void } +; CHECK-LABEL: define void @foo2() { +; CHECK: %call = tail call i32* @qux() +; CHECK-NEXT: %tcall = bitcast i32* %call to i8* +; CHECK-NEXT: call void asm sideeffect "mov\09r7, r7\09\09@ marker for return value optimization", ""() +; CHECK-NEXT: %0 = tail call i8* @objc_unsafeClaimAutoreleasedReturnValue(i8* %tcall) [[NUW:#[0-9]+]] +; CHECK: } + +define void @foo2() { +entry: + %call = tail call i32* @qux() + %tcall = bitcast i32* %call to i8* + %0 = tail call i8* @objc_unsafeClaimAutoreleasedReturnValue(i8* %tcall) nounwind + tail call void @bar(i8* %0) + ret void +} + + declare i32* @qux() declare i8* @objc_retainAutoreleasedReturnValue(i8*) +declare i8* @objc_unsafeClaimAutoreleasedReturnValue(i8*) declare void @bar(i8*) !clang.arc.retainAutoreleasedReturnValueMarker = !{!0} -!0 = !{!"mov\09r7, r7\09\09@ marker for objc_retainAutoreleaseReturnValue"} +!0 = !{!"mov\09r7, r7\09\09@ marker for return value optimization"} ; CHECK: attributes [[NUW]] = { nounwind } diff --git a/llvm/test/Transforms/ObjCARC/tail-call-invariant-enforcement.ll b/llvm/test/Transforms/ObjCARC/tail-call-invariant-enforcement.ll index 1ec61c84810..3073abf7bf5 100644 --- a/llvm/test/Transforms/ObjCARC/tail-call-invariant-enforcement.ll +++ b/llvm/test/Transforms/ObjCARC/tail-call-invariant-enforcement.ll @@ -5,6 +5,7 @@ declare i8* @objc_retain(i8* %x) declare i8* @objc_autorelease(i8* %x) declare i8* @objc_autoreleaseReturnValue(i8* %x) declare i8* @objc_retainAutoreleasedReturnValue(i8* %x) +declare i8* @objc_unsafeClaimAutoreleasedReturnValue(i8* %x) declare i8* @tmp(i8*) ; Never tail call objc_autorelease. @@ -85,5 +86,19 @@ entry: ret i8* %tmp0 } +; Always tail call objc_unsafeClaimAutoreleasedReturnValue. +; CHECK: define i8* @test6(i8* %x) [[NUW]] { +; CHECK: %tmp0 = tail call i8* @objc_unsafeClaimAutoreleasedReturnValue(i8* %y) [[NUW]] +; CHECK: %tmp1 = tail call i8* @objc_unsafeClaimAutoreleasedReturnValue(i8* %z) [[NUW]] +; CHECK: } +define i8* @test6(i8* %x) nounwind { +entry: + %y = call i8* @tmp(i8* %x) + %tmp0 = call i8* @objc_unsafeClaimAutoreleasedReturnValue(i8* %y) + %z = call i8* @tmp(i8* %x) + %tmp1 = tail call i8* @objc_unsafeClaimAutoreleasedReturnValue(i8* %z) + ret i8* %x +} + ; CHECK: attributes [[NUW]] = { nounwind } |