[objc-arc] Introduce the concept of RCIdentity and rename all relevant functions to use that name. NFC.

The RCIdentity root ("Reference Count Identity Root") of a value V is a
dominating value U for which retaining or releasing U is equivalent to
retaining or releasing V. In other words, ARC operations on V are
equivalent to ARC operations on U.

This is a useful property to ascertain since we can use this in the ARC
optimizer to make it easier to match up ARC operations by always mapping
ARC operations to RCIdentityRoots instead of pointers themselves. Then
we perform pairing of retains, releases which are applied to the same
RCIdentityRoot.

In general, the two ways that we see RCIdentical values in ObjC are via:

  1. PointerCasts
  2. Forwarding Calls that return their argument verbatim.

As such in ObjC, two RCIdentical pointers must always point to the same
memory location.

Previously this concept was implicit in the code and various methods
that dealt with this concept were given functional names that did not
conform to any name in the "ARC" model. This often times resulted in
code that was hard for the non-ARC acquanted to understand resulting in
unhappiness and confusion.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@229796 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Michael Gottesman 2015-02-19 00:42:38 +00:00
parent db011405b3
commit 8915e014e0
5 changed files with 60 additions and 53 deletions

View File

@ -171,7 +171,7 @@ llvm::objcarc::Depends(DependenceKind Flavor, Instruction *Inst,
case IC_Retain: case IC_Retain:
case IC_RetainRV: case IC_RetainRV:
// Check for a retain of the same pointer for merging. // Check for a retain of the same pointer for merging.
return GetObjCArg(Inst) == Arg; return GetArgRCIdentityRoot(Inst) == Arg;
default: default:
// Nothing else matters for objc_retainAutorelease formation. // Nothing else matters for objc_retainAutorelease formation.
return false; return false;
@ -183,7 +183,7 @@ llvm::objcarc::Depends(DependenceKind Flavor, Instruction *Inst,
case IC_Retain: case IC_Retain:
case IC_RetainRV: case IC_RetainRV:
// Check for a retain of the same pointer for merging. // Check for a retain of the same pointer for merging.
return GetObjCArg(Inst) == Arg; return GetArgRCIdentityRoot(Inst) == Arg;
default: default:
// Anything that can autorelease interrupts // Anything that can autorelease interrupts
// retainAutoreleaseReturnValue formation. // retainAutoreleaseReturnValue formation.

View File

@ -229,10 +229,22 @@ static inline const Value *GetUnderlyingObjCPtr(const Value *V) {
return V; return V;
} }
/// \brief This is a wrapper around Value::stripPointerCasts which also knows /// The RCIdentity root of a value \p V is a dominating value U for which
/// how to look through objc_retain and objc_autorelease calls, which we know to /// retaining or releasing U is equivalent to retaining or releasing V. In other
/// return their argument verbatim. /// words, ARC operations on \p V are equivalent to ARC operations on \p U.
static inline const Value *StripPointerCastsAndObjCCalls(const Value *V) { ///
/// We use this in the ARC optimizer to make it easier to match up ARC
/// operations by always mapping ARC operations to RCIdentityRoots instead of
/// pointers themselves.
///
/// The two ways that we see RCIdentical values in ObjC are via:
///
/// 1. PointerCasts
/// 2. Forwarding Calls that return their argument verbatim.
///
/// Thus this function strips off pointer casts and forwarding calls. *NOTE*
/// This implies that two RCIdentical values must alias.
static inline const Value *GetRCIdentityRoot(const Value *V) {
for (;;) { for (;;) {
V = V->stripPointerCasts(); V = V->stripPointerCasts();
if (!IsForwarding(GetBasicInstructionClass(V))) if (!IsForwarding(GetBasicInstructionClass(V)))
@ -242,24 +254,19 @@ static inline const Value *StripPointerCastsAndObjCCalls(const Value *V) {
return V; return V;
} }
/// \brief This is a wrapper around Value::stripPointerCasts which also knows /// Helper which calls const Value *GetRCIdentityRoot(const Value *V) and just
/// how to look through objc_retain and objc_autorelease calls, which we know to /// casts away the const of the result. For documentation about what an
/// return their argument verbatim. /// RCIdentityRoot (and by extension GetRCIdentityRoot is) look at that
static inline Value *StripPointerCastsAndObjCCalls(Value *V) { /// function.
for (;;) { static inline Value *GetRCIdentityRoot(Value *V) {
V = V->stripPointerCasts(); return const_cast<Value *>(GetRCIdentityRoot((const Value *)V));
if (!IsForwarding(GetBasicInstructionClass(V)))
break;
V = cast<CallInst>(V)->getArgOperand(0);
}
return V;
} }
/// \brief Assuming the given instruction is one of the special calls such as /// \brief Assuming the given instruction is one of the special calls such as
/// objc_retain or objc_release, return the argument value, stripped of no-op /// objc_retain or objc_release, return the RCIdentity root of the argument of
/// casts and forwarding calls. /// the call.
static inline Value *GetObjCArg(Value *Inst) { static inline Value *GetArgRCIdentityRoot(Value *Inst) {
return StripPointerCastsAndObjCCalls(cast<CallInst>(Inst)->getArgOperand(0)); return GetRCIdentityRoot(cast<CallInst>(Inst)->getArgOperand(0));
} }
static inline bool IsNullOrUndef(const Value *V) { static inline bool IsNullOrUndef(const Value *V) {
@ -371,7 +378,7 @@ static inline bool IsObjCIdentifiedObject(const Value *V) {
if (const LoadInst *LI = dyn_cast<LoadInst>(V)) { if (const LoadInst *LI = dyn_cast<LoadInst>(V)) {
const Value *Pointer = const Value *Pointer =
StripPointerCastsAndObjCCalls(LI->getPointerOperand()); GetRCIdentityRoot(LI->getPointerOperand());
if (const GlobalVariable *GV = dyn_cast<GlobalVariable>(Pointer)) { if (const GlobalVariable *GV = dyn_cast<GlobalVariable>(Pointer)) {
// A constant pointer can't be pointing to an object on the heap. It may // A constant pointer can't be pointing to an object on the heap. It may
// be reference-counted, but it won't be deleted. // be reference-counted, but it won't be deleted.

View File

@ -59,8 +59,8 @@ ObjCARCAliasAnalysis::alias(const Location &LocA, const Location &LocB) {
// First, strip off no-ops, including ObjC-specific no-ops, and try making a // First, strip off no-ops, including ObjC-specific no-ops, and try making a
// precise alias query. // precise alias query.
const Value *SA = StripPointerCastsAndObjCCalls(LocA.Ptr); const Value *SA = GetRCIdentityRoot(LocA.Ptr);
const Value *SB = StripPointerCastsAndObjCCalls(LocB.Ptr); const Value *SB = GetRCIdentityRoot(LocB.Ptr);
AliasResult Result = AliasResult Result =
AliasAnalysis::alias(Location(SA, LocA.Size, LocA.AATags), AliasAnalysis::alias(Location(SA, LocA.Size, LocA.AATags),
Location(SB, LocB.Size, LocB.AATags)); Location(SB, LocB.Size, LocB.AATags));
@ -92,7 +92,7 @@ ObjCARCAliasAnalysis::pointsToConstantMemory(const Location &Loc,
// First, strip off no-ops, including ObjC-specific no-ops, and try making // First, strip off no-ops, including ObjC-specific no-ops, and try making
// a precise alias query. // a precise alias query.
const Value *S = StripPointerCastsAndObjCCalls(Loc.Ptr); const Value *S = GetRCIdentityRoot(Loc.Ptr);
if (AliasAnalysis::pointsToConstantMemory(Location(S, Loc.Size, Loc.AATags), if (AliasAnalysis::pointsToConstantMemory(Location(S, Loc.Size, Loc.AATags),
OrLocal)) OrLocal))
return true; return true;

View File

@ -110,7 +110,7 @@ namespace {
/// return value. We do this late so we do not disrupt the dataflow analysis in /// return value. We do this late so we do not disrupt the dataflow analysis in
/// ObjCARCOpt. /// ObjCARCOpt.
bool ObjCARCContract::optimizeRetainCall(Function &F, Instruction *Retain) { bool ObjCARCContract::optimizeRetainCall(Function &F, Instruction *Retain) {
ImmutableCallSite CS(GetObjCArg(Retain)); ImmutableCallSite CS(GetArgRCIdentityRoot(Retain));
const Instruction *Call = CS.getInstruction(); const Instruction *Call = CS.getInstruction();
if (!Call) if (!Call)
return false; return false;
@ -146,7 +146,7 @@ bool ObjCARCContract::contractAutorelease(
Function &F, Instruction *Autorelease, InstructionClass Class, Function &F, Instruction *Autorelease, InstructionClass Class,
SmallPtrSetImpl<Instruction *> &DependingInstructions, SmallPtrSetImpl<Instruction *> &DependingInstructions,
SmallPtrSetImpl<const BasicBlock *> &Visited) { SmallPtrSetImpl<const BasicBlock *> &Visited) {
const Value *Arg = GetObjCArg(Autorelease); const Value *Arg = GetArgRCIdentityRoot(Autorelease);
// Check that there are no instructions between the retain and the autorelease // Check that there are no instructions between the retain and the autorelease
// (such as an autorelease_pop) which may change the count. // (such as an autorelease_pop) which may change the count.
@ -171,7 +171,7 @@ bool ObjCARCContract::contractAutorelease(
if (!Retain || if (!Retain ||
GetBasicInstructionClass(Retain) != IC_Retain || GetBasicInstructionClass(Retain) != IC_Retain ||
GetObjCArg(Retain) != Arg) GetArgRCIdentityRoot(Retain) != Arg)
return false; return false;
Changed = true; Changed = true;
@ -199,7 +199,7 @@ bool ObjCARCContract::contractAutorelease(
void void
ObjCARCContract:: ObjCARCContract::
tryToContractReleaseIntoStoreStrong(Instruction *Release, inst_iterator &Iter) { tryToContractReleaseIntoStoreStrong(Instruction *Release, inst_iterator &Iter) {
LoadInst *Load = dyn_cast<LoadInst>(GetObjCArg(Release)); LoadInst *Load = dyn_cast<LoadInst>(GetArgRCIdentityRoot(Release));
if (!Load || !Load->isSimple()) return; if (!Load || !Load->isSimple()) return;
// For now, require everything to be in one basic block. // For now, require everything to be in one basic block.
@ -242,7 +242,7 @@ tryToContractReleaseIntoStoreStrong(Instruction *Release, inst_iterator &Iter) {
} }
} }
Value *New = StripPointerCastsAndObjCCalls(Store->getValueOperand()); Value *New = GetRCIdentityRoot(Store->getValueOperand());
// Walk up to find the retain. // Walk up to find the retain.
I = Store; I = Store;
@ -251,7 +251,7 @@ tryToContractReleaseIntoStoreStrong(Instruction *Release, inst_iterator &Iter) {
--I; --I;
Instruction *Retain = I; Instruction *Retain = I;
if (GetBasicInstructionClass(Retain) != IC_Retain) return; if (GetBasicInstructionClass(Retain) != IC_Retain) return;
if (GetObjCArg(Retain) != New) return; if (GetArgRCIdentityRoot(Retain) != New) return;
Changed = true; Changed = true;
++NumStoreStrongs; ++NumStoreStrongs;
@ -338,7 +338,7 @@ bool ObjCARCContract::tryToPeepholeInstruction(
--BBI; --BBI;
} while (IsNoopInstruction(BBI)); } while (IsNoopInstruction(BBI));
if (&*BBI == GetObjCArg(Inst)) { if (&*BBI == GetArgRCIdentityRoot(Inst)) {
DEBUG(dbgs() << "Adding inline asm marker for " DEBUG(dbgs() << "Adding inline asm marker for "
"retainAutoreleasedReturnValue optimization.\n"); "retainAutoreleasedReturnValue optimization.\n");
Changed = true; Changed = true;
@ -436,7 +436,7 @@ bool ObjCARCContract::runOnFunction(Function &F) {
// Otherwise, try to undo objc-arc-expand. // Otherwise, try to undo objc-arc-expand.
// Don't use GetObjCArg because we don't want to look through bitcasts // Don't use GetArgRCIdentityRoot because we don't want to look through bitcasts
// and such; to do the replacement, the argument must have type i8*. // and such; to do the replacement, the argument must have type i8*.
Value *Arg = cast<CallInst>(Inst)->getArgOperand(0); Value *Arg = cast<CallInst>(Inst)->getArgOperand(0);
@ -457,7 +457,7 @@ bool ObjCARCContract::runOnFunction(Function &F) {
// reachability here because an unreachable call is considered to // reachability here because an unreachable call is considered to
// trivially dominate itself, which would lead us to rewriting its // trivially dominate itself, which would lead us to rewriting its
// argument in terms of its return value, which would lead to // argument in terms of its return value, which would lead to
// infinite loops in GetObjCArg. // infinite loops in GetArgRCIdentityRoot.
if (DT->isReachableFromEntry(U) && DT->dominates(Inst, U)) { if (DT->isReachableFromEntry(U) && DT->dominates(Inst, U)) {
Changed = true; Changed = true;
Instruction *Replacement = Inst; Instruction *Replacement = Inst;

View File

@ -144,7 +144,7 @@ namespace {
/// \defgroup ARCUtilities Utility declarations/definitions specific to ARC. /// \defgroup ARCUtilities Utility declarations/definitions specific to ARC.
/// @{ /// @{
/// \brief This is similar to StripPointerCastsAndObjCCalls but it stops as soon /// \brief This is similar to GetRCIdentityRoot but it stops as soon
/// as it finds a value with multiple uses. /// as it finds a value with multiple uses.
static const Value *FindSingleUseIdentifiedObject(const Value *Arg) { static const Value *FindSingleUseIdentifiedObject(const Value *Arg) {
if (Arg->hasOneUse()) { if (Arg->hasOneUse()) {
@ -165,7 +165,7 @@ static const Value *FindSingleUseIdentifiedObject(const Value *Arg) {
// trivial uses, we can still consider this to be a single-use value. // trivial uses, we can still consider this to be a single-use value.
if (IsObjCIdentifiedObject(Arg)) { if (IsObjCIdentifiedObject(Arg)) {
for (const User *U : Arg->users()) for (const User *U : Arg->users())
if (!U->use_empty() || StripPointerCastsAndObjCCalls(U) != Arg) if (!U->use_empty() || GetRCIdentityRoot(U) != Arg)
return nullptr; return nullptr;
return Arg; return Arg;
@ -1191,7 +1191,7 @@ void ObjCARCOpt::getAnalysisUsage(AnalysisUsage &AU) const {
bool bool
ObjCARCOpt::OptimizeRetainRVCall(Function &F, Instruction *RetainRV) { ObjCARCOpt::OptimizeRetainRVCall(Function &F, Instruction *RetainRV) {
// Check for the argument being from an immediately preceding call or invoke. // Check for the argument being from an immediately preceding call or invoke.
const Value *Arg = GetObjCArg(RetainRV); const Value *Arg = GetArgRCIdentityRoot(RetainRV);
ImmutableCallSite CS(Arg); ImmutableCallSite CS(Arg);
if (const Instruction *Call = CS.getInstruction()) { if (const Instruction *Call = CS.getInstruction()) {
if (Call->getParent() == RetainRV->getParent()) { if (Call->getParent() == RetainRV->getParent()) {
@ -1217,7 +1217,7 @@ ObjCARCOpt::OptimizeRetainRVCall(Function &F, Instruction *RetainRV) {
if (I != Begin) { if (I != Begin) {
do --I; while (I != Begin && IsNoopInstruction(I)); do --I; while (I != Begin && IsNoopInstruction(I));
if (GetBasicInstructionClass(I) == IC_AutoreleaseRV && if (GetBasicInstructionClass(I) == IC_AutoreleaseRV &&
GetObjCArg(I) == Arg) { GetArgRCIdentityRoot(I) == Arg) {
Changed = true; Changed = true;
++NumPeeps; ++NumPeeps;
@ -1252,7 +1252,7 @@ void
ObjCARCOpt::OptimizeAutoreleaseRVCall(Function &F, Instruction *AutoreleaseRV, ObjCARCOpt::OptimizeAutoreleaseRVCall(Function &F, Instruction *AutoreleaseRV,
InstructionClass &Class) { InstructionClass &Class) {
// Check for a return of the pointer value. // Check for a return of the pointer value.
const Value *Ptr = GetObjCArg(AutoreleaseRV); const Value *Ptr = GetArgRCIdentityRoot(AutoreleaseRV);
SmallVector<const Value *, 2> Users; SmallVector<const Value *, 2> Users;
Users.push_back(Ptr); Users.push_back(Ptr);
do { do {
@ -1426,7 +1426,7 @@ void ObjCARCOpt::OptimizeIndividualCalls(Function &F) {
continue; continue;
} }
const Value *Arg = GetObjCArg(Inst); const Value *Arg = GetArgRCIdentityRoot(Inst);
// ARC calls with null are no-ops. Delete them. // ARC calls with null are no-ops. Delete them.
if (IsNullOrUndef(Arg)) { if (IsNullOrUndef(Arg)) {
@ -1463,7 +1463,7 @@ void ObjCARCOpt::OptimizeIndividualCalls(Function &F) {
bool HasCriticalEdges = false; bool HasCriticalEdges = false;
for (unsigned i = 0, e = PN->getNumIncomingValues(); i != e; ++i) { for (unsigned i = 0, e = PN->getNumIncomingValues(); i != e; ++i) {
Value *Incoming = Value *Incoming =
StripPointerCastsAndObjCCalls(PN->getIncomingValue(i)); GetRCIdentityRoot(PN->getIncomingValue(i));
if (IsNullOrUndef(Incoming)) if (IsNullOrUndef(Incoming))
HasNull = true; HasNull = true;
else if (cast<TerminatorInst>(PN->getIncomingBlock(i)->back()) else if (cast<TerminatorInst>(PN->getIncomingBlock(i)->back())
@ -1517,7 +1517,7 @@ void ObjCARCOpt::OptimizeIndividualCalls(Function &F) {
Type *ParamTy = CInst->getArgOperand(0)->getType(); Type *ParamTy = CInst->getArgOperand(0)->getType();
for (unsigned i = 0, e = PN->getNumIncomingValues(); i != e; ++i) { for (unsigned i = 0, e = PN->getNumIncomingValues(); i != e; ++i) {
Value *Incoming = Value *Incoming =
StripPointerCastsAndObjCCalls(PN->getIncomingValue(i)); GetRCIdentityRoot(PN->getIncomingValue(i));
if (!IsNullOrUndef(Incoming)) { if (!IsNullOrUndef(Incoming)) {
CallInst *Clone = cast<CallInst>(CInst->clone()); CallInst *Clone = cast<CallInst>(CInst->clone());
Value *Op = PN->getIncomingValue(i); Value *Op = PN->getIncomingValue(i);
@ -1718,7 +1718,7 @@ ObjCARCOpt::VisitInstructionBottomUp(Instruction *Inst,
switch (Class) { switch (Class) {
case IC_Release: { case IC_Release: {
Arg = GetObjCArg(Inst); Arg = GetArgRCIdentityRoot(Inst);
PtrState &S = MyStates.getPtrBottomUpState(Arg); PtrState &S = MyStates.getPtrBottomUpState(Arg);
@ -1752,7 +1752,7 @@ ObjCARCOpt::VisitInstructionBottomUp(Instruction *Inst,
break; break;
case IC_Retain: case IC_Retain:
case IC_RetainRV: { case IC_RetainRV: {
Arg = GetObjCArg(Inst); Arg = GetArgRCIdentityRoot(Inst);
PtrState &S = MyStates.getPtrBottomUpState(Arg); PtrState &S = MyStates.getPtrBottomUpState(Arg);
S.SetKnownPositiveRefCount(); S.SetKnownPositiveRefCount();
@ -1808,7 +1808,7 @@ ObjCARCOpt::VisitInstructionBottomUp(Instruction *Inst,
if (StoreInst *SI = dyn_cast<StoreInst>(Inst)) { if (StoreInst *SI = dyn_cast<StoreInst>(Inst)) {
if (AreAnyUnderlyingObjectsAnAlloca(SI->getPointerOperand())) { if (AreAnyUnderlyingObjectsAnAlloca(SI->getPointerOperand())) {
BBState::ptr_iterator I = MyStates.findPtrBottomUpState( BBState::ptr_iterator I = MyStates.findPtrBottomUpState(
StripPointerCastsAndObjCCalls(SI->getValueOperand())); GetRCIdentityRoot(SI->getValueOperand()));
if (I != MyStates.bottom_up_ptr_end()) if (I != MyStates.bottom_up_ptr_end())
MultiOwnersSet.insert(I->first); MultiOwnersSet.insert(I->first);
} }
@ -1978,7 +1978,7 @@ ObjCARCOpt::VisitInstructionTopDown(Instruction *Inst,
break; break;
case IC_Retain: case IC_Retain:
case IC_RetainRV: { case IC_RetainRV: {
Arg = GetObjCArg(Inst); Arg = GetArgRCIdentityRoot(Inst);
PtrState &S = MyStates.getPtrTopDownState(Arg); PtrState &S = MyStates.getPtrTopDownState(Arg);
@ -2008,7 +2008,7 @@ ObjCARCOpt::VisitInstructionTopDown(Instruction *Inst,
break; break;
} }
case IC_Release: { case IC_Release: {
Arg = GetObjCArg(Inst); Arg = GetArgRCIdentityRoot(Inst);
PtrState &S = MyStates.getPtrTopDownState(Arg); PtrState &S = MyStates.getPtrTopDownState(Arg);
S.ClearKnownPositiveRefCount(); S.ClearKnownPositiveRefCount();
@ -2372,7 +2372,7 @@ ObjCARCOpt::ConnectTDBUTraversals(DenseMap<const BasicBlock *, BBState>
const RRInfo &NewRetainRRI = It->second; const RRInfo &NewRetainRRI = It->second;
KnownSafeTD &= NewRetainRRI.KnownSafe; KnownSafeTD &= NewRetainRRI.KnownSafe;
MultipleOwners = MultipleOwners =
MultipleOwners || MultiOwnersSet.count(GetObjCArg(NewRetain)); MultipleOwners || MultiOwnersSet.count(GetArgRCIdentityRoot(NewRetain));
for (Instruction *NewRetainRelease : NewRetainRRI.Calls) { for (Instruction *NewRetainRelease : NewRetainRRI.Calls) {
DenseMap<Value *, RRInfo>::const_iterator Jt = DenseMap<Value *, RRInfo>::const_iterator Jt =
Releases.find(NewRetainRelease); Releases.find(NewRetainRelease);
@ -2581,7 +2581,7 @@ ObjCARCOpt::PerformCodePlacement(DenseMap<const BasicBlock *, BBState>
DEBUG(dbgs() << "Visiting: " << *Retain << "\n"); DEBUG(dbgs() << "Visiting: " << *Retain << "\n");
Value *Arg = GetObjCArg(Retain); Value *Arg = GetArgRCIdentityRoot(Retain);
// If the object being released is in static or stack storage, we know it's // If the object being released is in static or stack storage, we know it's
// not being managed by ObjC reference counting, so we can delete pairs // not being managed by ObjC reference counting, so we can delete pairs
@ -2593,7 +2593,7 @@ ObjCARCOpt::PerformCodePlacement(DenseMap<const BasicBlock *, BBState>
if (const LoadInst *LI = dyn_cast<LoadInst>(Arg)) if (const LoadInst *LI = dyn_cast<LoadInst>(Arg))
if (const GlobalVariable *GV = if (const GlobalVariable *GV =
dyn_cast<GlobalVariable>( dyn_cast<GlobalVariable>(
StripPointerCastsAndObjCCalls(LI->getPointerOperand()))) GetRCIdentityRoot(LI->getPointerOperand())))
if (GV->isConstant()) if (GV->isConstant())
KnownSafe = true; KnownSafe = true;
@ -2860,7 +2860,7 @@ FindPredecessorRetainWithSafePath(const Value *Arg, BasicBlock *BB,
// Check that we found a retain with the same argument. // Check that we found a retain with the same argument.
if (!Retain || if (!Retain ||
!IsRetain(GetBasicInstructionClass(Retain)) || !IsRetain(GetBasicInstructionClass(Retain)) ||
GetObjCArg(Retain) != Arg) { GetArgRCIdentityRoot(Retain) != Arg) {
return nullptr; return nullptr;
} }
@ -2888,7 +2888,7 @@ FindPredecessorAutoreleaseWithSafePath(const Value *Arg, BasicBlock *BB,
InstructionClass AutoreleaseClass = GetBasicInstructionClass(Autorelease); InstructionClass AutoreleaseClass = GetBasicInstructionClass(Autorelease);
if (!IsAutorelease(AutoreleaseClass)) if (!IsAutorelease(AutoreleaseClass))
return nullptr; return nullptr;
if (GetObjCArg(Autorelease) != Arg) if (GetArgRCIdentityRoot(Autorelease) != Arg)
return nullptr; return nullptr;
return Autorelease; return Autorelease;
@ -2919,7 +2919,7 @@ void ObjCARCOpt::OptimizeReturns(Function &F) {
if (!Ret) if (!Ret)
continue; continue;
const Value *Arg = StripPointerCastsAndObjCCalls(Ret->getOperand(0)); const Value *Arg = GetRCIdentityRoot(Ret->getOperand(0));
// Look for an ``autorelease'' instruction that is a predecessor of Ret and // Look for an ``autorelease'' instruction that is a predecessor of Ret and
// dependent on Arg such that there are no instructions dependent on Arg // dependent on Arg such that there are no instructions dependent on Arg