Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 71 additions & 4 deletions llvm/lib/Transforms/ObjCARC/ObjCARCOpts.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,63 @@ static const Value *FindSingleUseIdentifiedObject(const Value *Arg) {
/// \defgroup ARCOpt ARC Optimization.
/// @{

/// Check if there is an autoreleasePoolPop after the given autorelease
/// instruction in the same basic block with no intervening calls that
/// could affect the autorelease pool.
static bool HasFollowingAutoreleasePoolPop(Instruction *AutoreleaseInst) {
assert(IsAutorelease(GetBasicARCInstKind(AutoreleaseInst)));
BasicBlock *BB = AutoreleaseInst->getParent();

// Look forward from the autorelease instruction
for (BasicBlock::iterator I = std::next(AutoreleaseInst->getIterator()),
E = BB->end();
I != E; ++I) {
ARCInstKind Class = GetBasicARCInstKind(&*I);

switch (Class) {
case ARCInstKind::AutoreleasepoolPop:
// Found a pool pop - the autorelease will be drained
return true;

case ARCInstKind::AutoreleasepoolPush:
// A new pool started - this autorelease won't be drained by a later pop
return false;

case ARCInstKind::CallOrUser:
case ARCInstKind::Call:
// Unknown call could affect autorelease pool state or return autoreleased
// objects
return false;
case ARCInstKind::Retain:
case ARCInstKind::RetainRV:
case ARCInstKind::UnsafeClaimRV:
case ARCInstKind::RetainBlock:
case ARCInstKind::Release:
case ARCInstKind::Autorelease:
case ARCInstKind::AutoreleaseRV:
case ARCInstKind::NoopCast:
case ARCInstKind::FusedRetainAutorelease:
case ARCInstKind::FusedRetainAutoreleaseRV:
case ARCInstKind::LoadWeakRetained:
case ARCInstKind::StoreWeak:
case ARCInstKind::InitWeak:
case ARCInstKind::LoadWeak:
case ARCInstKind::MoveWeak:
case ARCInstKind::CopyWeak:
case ARCInstKind::DestroyWeak:
case ARCInstKind::StoreStrong:
case ARCInstKind::IntrinsicUser:
case ARCInstKind::User:
case ARCInstKind::None:
// Everything else is safe to ignore:
break;
}
}

// Reached end of basic block without finding a pool pop
return false;
}

// TODO: On code like this:
//
// objc_retain(%x)
Expand Down Expand Up @@ -978,12 +1035,22 @@ void ObjCARCOpt::OptimizeIndividualCallImpl(Function &F, Instruction *Inst,
break;
}

// objc_autorelease(x) -> objc_release(x) if x is otherwise unused.
// objc_autorelease(x) -> objc_release(x) if x is otherwise unused
// OR if this autorelease is followed by an autoreleasePoolPop.
if (IsAutorelease(Class) && Inst->use_empty()) {
CallInst *Call = cast<CallInst>(Inst);
const Value *Arg = Call->getArgOperand(0);
Arg = FindSingleUseIdentifiedObject(Arg);
if (Arg) {
bool ShouldConvert = (Arg != nullptr);
const char *Reason = "since x is otherwise unused";

// Also convert if this autorelease is followed by a pool pop
if (!ShouldConvert && HasFollowingAutoreleasePoolPop(Inst)) {
ShouldConvert = true;
Reason = "since it's followed by autoreleasePoolPop";
}

if (ShouldConvert) {
Changed = true;
++NumAutoreleases;

Expand All @@ -997,8 +1064,8 @@ void ObjCARCOpt::OptimizeIndividualCallImpl(Function &F, Instruction *Inst,
MDNode::get(C, {}));

LLVM_DEBUG(dbgs() << "Replacing autorelease{,RV}(x) with objc_release(x) "
"since x is otherwise unused.\nOld: "
<< *Call << "\nNew: " << *NewCall << "\n");
<< Reason << ".\nOld: " << *Call
<< "\nNew: " << *NewCall << "\n");

EraseInstruction(Call);
Inst = NewCall;
Expand Down
6 changes: 2 additions & 4 deletions llvm/test/Transforms/ObjCARC/test_autorelease_pool.ll
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ define void @test_multiple_autoreleases() {
; CHECK-NEXT: call void @use_object(ptr [[OBJ1]])
; CHECK-NEXT: [[TMP1:%.*]] = call ptr @llvm.objc.autorelease(ptr [[OBJ1]]) #[[ATTR0]]
; CHECK-NEXT: call void @use_object(ptr [[OBJ2]])
; CHECK-NEXT: [[TMP2:%.*]] = call ptr @llvm.objc.autorelease(ptr [[OBJ2]]) #[[ATTR0]]
; CHECK-NEXT: call void @llvm.objc.release(ptr [[OBJ2]]) #[[ATTR0]], !clang.imprecise_release [[META0]]
; CHECK-NEXT: call void @llvm.objc.autoreleasePoolPop(ptr [[POOL]]) #[[ATTR0]]
; CHECK-NEXT: ret void
;
Expand Down Expand Up @@ -211,9 +211,7 @@ define void @test_complex_shadowing() {
; CHECK-NEXT: [[OBJ3:%.*]] = call ptr @create_object()
; CHECK-NEXT: call void @llvm.objc.release(ptr [[OBJ1]]) #[[ATTR0]], !clang.imprecise_release [[META0]]
; CHECK-NEXT: call void @llvm.objc.release(ptr [[OBJ2]]) #[[ATTR0]], !clang.imprecise_release [[META0]]
; CHECK-NEXT: [[INNER2_POOL:%.*]] = call ptr @llvm.objc.autoreleasePoolPush() #[[ATTR0]]
; CHECK-NEXT: [[TMP1:%.*]] = call ptr @llvm.objc.autorelease(ptr [[OBJ3]]) #[[ATTR0]]
; CHECK-NEXT: call void @llvm.objc.autoreleasePoolPop(ptr [[INNER2_POOL]]) #[[ATTR0]]
; CHECK-NEXT: call void @llvm.objc.release(ptr [[OBJ3]]) #[[ATTR0]], !clang.imprecise_release [[META0]]
; CHECK-NEXT: ret void
;
%obj1 = call ptr @create_object()
Expand Down