mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2025-01-31 20:51:52 +01:00
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
This commit is contained in:
parent
164a0ed653
commit
02db7c55a3
@ -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") ||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
||||
|
@ -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 }
|
||||
|
@ -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 }
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user