Skip to content

Commit 3279724

Browse files
committed
llvm/ObjCARC: Eliminate inlined AutoreleaseRV calls
Pair up inlined AutoreleaseRV calls with their matching RetainRV or ClaimRV. - RetainRV cancels out AutoreleaseRV. Delete both instructions. - ClaimRV is a peephole for RetainRV+Release. Delete AutoreleaseRV and replace ClaimRV with Release. This avoids problems where more aggressive inlining triggers memory regressions. This patch is happy to skip over non-callable instructions and non-ARC intrinsics looking for the pair. It is likely sound to also skip over opaque function calls, but that's harder to reason about, and it's not relevant to the goal here: if there's an opaque function call splitting up a pair, it's very unlikely that a handshake would have happened dynamically without inlining. Note that this patch also subsumes the previous logic that looked backwards from ReleaseRV. https://reviews.llvm.org/D70370 rdar://problem/46509586
1 parent 0a8e7ca commit 3279724

File tree

3 files changed

+438
-73
lines changed

3 files changed

+438
-73
lines changed

llvm/lib/Transforms/ObjCARC/ObjCARCOpts.cpp

Lines changed: 145 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -507,10 +507,20 @@ namespace {
507507
void OptimizeAutoreleaseRVCall(Function &F, Instruction *AutoreleaseRV,
508508
ARCInstKind &Class);
509509
void OptimizeIndividualCalls(Function &F);
510-
void
511-
OptimizeIndividualCallImpl(Function &F,
512-
DenseMap<BasicBlock *, ColorVector> &BlockColors,
513-
Instruction *Inst, ARCInstKind Class);
510+
511+
/// Optimize an individual call, optionally passing the
512+
/// GetArgRCIdentityRoot if it has already been computed.
513+
void OptimizeIndividualCallImpl(
514+
Function &F, DenseMap<BasicBlock *, ColorVector> &BlockColors,
515+
Instruction *Inst, ARCInstKind Class, const Value *Arg);
516+
517+
/// Try to optimize an AutoreleaseRV with a RetainRV or ClaimRV. If the
518+
/// optimization occurs, returns true to indicate that the caller should
519+
/// assume the instructions are dead.
520+
bool OptimizeInlinedAutoreleaseRVCall(
521+
Function &F, DenseMap<BasicBlock *, ColorVector> &BlockColors,
522+
Instruction *Inst, const Value *&Arg, ARCInstKind Class,
523+
Instruction *AutoreleaseRV, const Value *&AutoreleaseRVArg);
514524

515525
void CheckForCFGHazards(const BasicBlock *BB,
516526
DenseMap<const BasicBlock *, BBState> &BBStates,
@@ -594,36 +604,8 @@ void ObjCARCOpt::getAnalysisUsage(AnalysisUsage &AU) const {
594604
AU.setPreservesCFG();
595605
}
596606

597-
static bool isSafeBetweenRVCalls(const Instruction *I) {
598-
if (IsNoopInstruction(I))
599-
return true;
600-
601-
auto *CB = dyn_cast<CallBase>(I);
602-
if (!CB)
603-
return false;
604-
605-
Intrinsic::ID IID = CB->getIntrinsicID();
606-
if (IID == Intrinsic::not_intrinsic)
607-
return false;
608-
609-
switch (IID) {
610-
case Intrinsic::lifetime_start:
611-
case Intrinsic::lifetime_end:
612-
// The inliner adds new lifetime markers as part of the return sequence,
613-
// which should be skipped when looking for paired return RV call.
614-
LLVM_FALLTHROUGH;
615-
case Intrinsic::stacksave:
616-
case Intrinsic::stackrestore:
617-
// If the inlined code contains dynamic allocas, the above applies as well.
618-
return true;
619-
default:
620-
return false;
621-
}
622-
}
623-
624607
/// Turn objc_retainAutoreleasedReturnValue into objc_retain if the operand is
625-
/// not a return value. Or, if it can be paired with an
626-
/// objc_autoreleaseReturnValue, delete the pair and return true.
608+
/// not a return value.
627609
bool
628610
ObjCARCOpt::OptimizeRetainRVCall(Function &F, Instruction *RetainRV) {
629611
// Check for the argument being from an immediately preceding call or invoke.
@@ -649,39 +631,6 @@ ObjCARCOpt::OptimizeRetainRVCall(Function &F, Instruction *RetainRV) {
649631
}
650632
}
651633

652-
// Track PHIs which are equivalent to our Arg.
653-
SmallDenseSet<const Value*, 2> EquivalentArgs;
654-
EquivalentArgs.insert(Arg);
655-
656-
// Add PHIs that are equivalent to Arg to ArgUsers.
657-
if (const PHINode *PN = dyn_cast<PHINode>(Arg)) {
658-
SmallVector<const Value *, 2> ArgUsers;
659-
getEquivalentPHIs(*PN, ArgUsers);
660-
EquivalentArgs.insert(ArgUsers.begin(), ArgUsers.end());
661-
}
662-
663-
// Check for being preceded by an objc_autoreleaseReturnValue on the same
664-
// pointer. In this case, we can delete the pair.
665-
BasicBlock::iterator I = RetainRV->getIterator(),
666-
Begin = RetainRV->getParent()->begin();
667-
if (I != Begin) {
668-
do
669-
--I;
670-
while (I != Begin && isSafeBetweenRVCalls(&*I));
671-
if (GetBasicARCInstKind(&*I) == ARCInstKind::AutoreleaseRV &&
672-
EquivalentArgs.count(GetArgRCIdentityRoot(&*I))) {
673-
Changed = true;
674-
++NumPeeps;
675-
676-
LLVM_DEBUG(dbgs() << "Erasing autoreleaseRV,retainRV pair: " << *I << "\n"
677-
<< "Erasing " << *RetainRV << "\n");
678-
679-
EraseInstruction(&*I);
680-
EraseInstruction(RetainRV);
681-
return true;
682-
}
683-
}
684-
685634
// Turn it to a plain objc_retain.
686635
Changed = true;
687636
++NumPeeps;
@@ -699,6 +648,62 @@ ObjCARCOpt::OptimizeRetainRVCall(Function &F, Instruction *RetainRV) {
699648
return false;
700649
}
701650

651+
bool ObjCARCOpt::OptimizeInlinedAutoreleaseRVCall(
652+
Function &F, DenseMap<BasicBlock *, ColorVector> &BlockColors,
653+
Instruction *Inst, const Value *&Arg, ARCInstKind Class,
654+
Instruction *AutoreleaseRV, const Value *&AutoreleaseRVArg) {
655+
// Must be in the same basic block.
656+
assert(Inst->getParent() == AutoreleaseRV->getParent());
657+
658+
// Must operate on the same root.
659+
Arg = GetArgRCIdentityRoot(Inst);
660+
AutoreleaseRVArg = GetArgRCIdentityRoot(AutoreleaseRV);
661+
if (Arg != AutoreleaseRVArg) {
662+
// If there isn't an exact match, check if we have equivalent PHIs.
663+
const PHINode *PN = dyn_cast<PHINode>(Arg);
664+
if (!PN)
665+
return false;
666+
667+
SmallVector<const Value *, 4> ArgUsers;
668+
getEquivalentPHIs(*PN, ArgUsers);
669+
if (llvm::find(ArgUsers, AutoreleaseRVArg) == ArgUsers.end())
670+
return false;
671+
}
672+
673+
// Okay, this is a match. Merge them.
674+
++NumPeeps;
675+
LLVM_DEBUG(dbgs() << "Found inlined objc_autoreleaseReturnValue '"
676+
<< *AutoreleaseRV << "' paired with '" << *Inst << "'\n");
677+
678+
// Delete the RV pair, starting with the AutoreleaseRV.
679+
AutoreleaseRV->replaceAllUsesWith(
680+
cast<CallInst>(AutoreleaseRV)->getArgOperand(0));
681+
EraseInstruction(AutoreleaseRV);
682+
if (Class == ARCInstKind::RetainRV) {
683+
// AutoreleaseRV and RetainRV cancel out. Delete the RetainRV.
684+
Inst->replaceAllUsesWith(cast<CallInst>(Inst)->getArgOperand(0));
685+
EraseInstruction(Inst);
686+
return true;
687+
}
688+
689+
// ClaimRV is a frontend peephole for RetainRV + Release. Since the
690+
// AutoreleaseRV and RetainRV cancel out, replace the ClaimRV with a Release.
691+
assert(Class == ARCInstKind::ClaimRV);
692+
Value *CallArg = cast<CallInst>(Inst)->getArgOperand(0);
693+
CallInst *Release = CallInst::Create(
694+
EP.get(ARCRuntimeEntryPointKind::Release), CallArg, "", Inst);
695+
assert(IsAlwaysTail(ARCInstKind::ClaimRV) &&
696+
"Expected ClaimRV to be safe to tail call");
697+
Release->setTailCall();
698+
Inst->replaceAllUsesWith(CallArg);
699+
EraseInstruction(Inst);
700+
701+
// Run the normal optimizations on Release.
702+
OptimizeIndividualCallImpl(F, BlockColors, Release, ARCInstKind::Release,
703+
Arg);
704+
return true;
705+
}
706+
702707
/// Turn objc_autoreleaseReturnValue into objc_autorelease if the result is not
703708
/// used as a return value.
704709
void ObjCARCOpt::OptimizeAutoreleaseRVCall(Function &F,
@@ -785,31 +790,98 @@ void ObjCARCOpt::OptimizeIndividualCalls(Function &F) {
785790
isScopedEHPersonality(classifyEHPersonality(F.getPersonalityFn())))
786791
BlockColors = colorEHFunclets(F);
787792

793+
// Store any delayed AutoreleaseRV intrinsics, so they can be easily paired
794+
// with RetainRV and ClaimRV.
795+
Instruction *DelayedAutoreleaseRV = nullptr;
796+
const Value *DelayedAutoreleaseRVArg = nullptr;
797+
auto setDelayedAutoreleaseRV = [&](Instruction *AutoreleaseRV) {
798+
assert(!DelayedAutoreleaseRV || !AutoreleaseRV);
799+
DelayedAutoreleaseRV = AutoreleaseRV;
800+
DelayedAutoreleaseRVArg = nullptr;
801+
};
802+
auto optimizeDelayedAutoreleaseRV = [&]() {
803+
if (!DelayedAutoreleaseRV)
804+
return;
805+
OptimizeIndividualCallImpl(F, BlockColors, DelayedAutoreleaseRV,
806+
ARCInstKind::AutoreleaseRV,
807+
DelayedAutoreleaseRVArg);
808+
setDelayedAutoreleaseRV(nullptr);
809+
};
810+
auto shouldDelayAutoreleaseRV = [&](Instruction *NonARCInst) {
811+
// Nothing to delay, but we may as well skip the logic below.
812+
if (!DelayedAutoreleaseRV)
813+
return true;
814+
815+
// If we hit the end of the basic block we're not going to find an RV-pair.
816+
// Stop delaying.
817+
if (NonARCInst->isTerminator())
818+
return false;
819+
820+
// Given the frontend rules for emitting AutoreleaseRV, RetainRV, and
821+
// ClaimRV, it's probably safe to skip over even opaque function calls
822+
// here since OptimizeInlinedAutoreleaseRVCall will confirm that they
823+
// have the same RCIdentityRoot. However, what really matters is
824+
// skipping instructions or intrinsics that the inliner could leave behind;
825+
// be conservative for now and don't skip over opaque calls, which could
826+
// potentially include other ARC calls.
827+
auto *CB = dyn_cast<CallBase>(NonARCInst);
828+
if (!CB)
829+
return true;
830+
return CB->getIntrinsicID() != Intrinsic::not_intrinsic;
831+
};
832+
788833
// Visit all objc_* calls in F.
789834
for (inst_iterator I = inst_begin(&F), E = inst_end(&F); I != E; ) {
790835
Instruction *Inst = &*I++;
791836

792837
ARCInstKind Class = GetBasicARCInstKind(Inst);
793838

794-
LLVM_DEBUG(dbgs() << "Visiting: Class: " << Class << "; " << *Inst << "\n");
795-
796839
// Skip this loop if this instruction isn't itself an ARC intrinsic.
840+
const Value *Arg = nullptr;
797841
switch (Class) {
798842
default:
843+
optimizeDelayedAutoreleaseRV();
799844
break;
800845
case ARCInstKind::CallOrUser:
801846
case ARCInstKind::User:
802847
case ARCInstKind::None:
848+
// This is a non-ARC instruction. If we're delaying an AutoreleaseRV,
849+
// check if it's safe to skip over it; if not, optimize the AutoreleaseRV
850+
// now.
851+
if (!shouldDelayAutoreleaseRV(Inst))
852+
optimizeDelayedAutoreleaseRV();
853+
continue;
854+
case ARCInstKind::AutoreleaseRV:
855+
optimizeDelayedAutoreleaseRV();
856+
setDelayedAutoreleaseRV(Inst);
803857
continue;
858+
case ARCInstKind::RetainRV:
859+
case ARCInstKind::ClaimRV:
860+
if (DelayedAutoreleaseRV) {
861+
// We have a potential RV pair. Check if they cancel out.
862+
if (OptimizeInlinedAutoreleaseRVCall(F, BlockColors, Inst, Arg, Class,
863+
DelayedAutoreleaseRV,
864+
DelayedAutoreleaseRVArg)) {
865+
setDelayedAutoreleaseRV(nullptr);
866+
continue;
867+
}
868+
optimizeDelayedAutoreleaseRV();
869+
}
870+
break;
804871
}
805872

806-
OptimizeIndividualCallImpl(F, BlockColors, Inst, Class);
873+
OptimizeIndividualCallImpl(F, BlockColors, Inst, Class, Arg);
807874
}
875+
876+
// Catch the final delayed AutoreleaseRV.
877+
optimizeDelayedAutoreleaseRV();
808878
}
809879

810880
void ObjCARCOpt::OptimizeIndividualCallImpl(
811881
Function &F, DenseMap<BasicBlock *, ColorVector> &BlockColors,
812-
Instruction *Inst, ARCInstKind Class) {
882+
Instruction *Inst, ARCInstKind Class, const Value *Arg) {
883+
LLVM_DEBUG(dbgs() << "Visiting: Class: " << Class << "; " << *Inst << "\n");
884+
813885
// Some of the ARC calls can be deleted if their arguments are global
814886
// variables that are inert in ARC.
815887
if (IsNoopOnGlobal(Class)) {
@@ -958,7 +1030,9 @@ void ObjCARCOpt::OptimizeIndividualCallImpl(
9581030
return;
9591031
}
9601032

961-
const Value *Arg = GetArgRCIdentityRoot(Inst);
1033+
// If we haven't already looked up the root, look it up now.
1034+
if (!Arg)
1035+
Arg = GetArgRCIdentityRoot(Inst);
9621036

9631037
// ARC calls with null are no-ops. Delete them.
9641038
if (IsNullOrUndef(Arg)) {

0 commit comments

Comments
 (0)