#define DEBUG_TYPE "objc-arc-opts"
+static cl::opt<unsigned> MaxPtrStates("arc-opt-max-ptr-states",
+ cl::Hidden,
+ cl::desc("Maximum number of ptr states the optimizer keeps track of"),
+ cl::init(4095));
+
/// \defgroup ARCUtilities Utility declarations/definitions specific to ARC.
/// @{
return !PerPtrTopDown.empty();
}
+ unsigned top_down_ptr_list_size() const {
+ return std::distance(top_down_ptr_begin(), top_down_ptr_end());
+ }
+
using bottom_up_ptr_iterator = decltype(PerPtrBottomUp)::iterator;
using const_bottom_up_ptr_iterator =
decltype(PerPtrBottomUp)::const_iterator;
return !PerPtrBottomUp.empty();
}
+ unsigned bottom_up_ptr_list_size() const {
+ return std::distance(bottom_up_ptr_begin(), bottom_up_ptr_end());
+ }
+
/// Mark this block as being an entry block, which has one path from the
/// entry by definition.
void SetAsEntry() { TopDownPathCount = 1; }
/// A flag indicating whether this optimization pass should run.
bool Run;
+ /// A flag indicating whether the optimization that removes or moves
+ /// retain/release pairs should be performed.
+ bool DisableRetainReleasePairing = false;
+
/// Flags which determine whether each of the interesting runtime functions
/// is in fact used in the current function.
unsigned UsedInThisFunction;
LLVM_DEBUG(dbgs() << " Visiting " << *Inst << "\n");
NestingDetected |= VisitInstructionBottomUp(Inst, BB, Retains, MyStates);
+
+ // Bail out if the number of pointers being tracked becomes too large so
+ // that this pass can complete in a reasonable amount of time.
+ if (MyStates.bottom_up_ptr_list_size() > MaxPtrStates) {
+ DisableRetainReleasePairing = true;
+ return false;
+ }
}
// If there's a predecessor with an invoke, visit the invoke as if it were
LLVM_DEBUG(dbgs() << " Visiting " << Inst << "\n");
NestingDetected |= VisitInstructionTopDown(&Inst, Releases, MyStates);
+
+ // Bail out if the number of pointers being tracked becomes too large so
+ // that this pass can complete in a reasonable amount of time.
+ if (MyStates.top_down_ptr_list_size() > MaxPtrStates) {
+ DisableRetainReleasePairing = true;
+ return false;
+ }
}
LLVM_DEBUG(dbgs() << "\nState Before Checking for CFG Hazards:\n"
// Use reverse-postorder on the reverse CFG for bottom-up.
bool BottomUpNestingDetected = false;
- for (BasicBlock *BB : llvm::reverse(ReverseCFGPostOrder))
+ for (BasicBlock *BB : llvm::reverse(ReverseCFGPostOrder)) {
BottomUpNestingDetected |= VisitBottomUp(BB, BBStates, Retains);
+ if (DisableRetainReleasePairing)
+ return false;
+ }
// Use reverse-postorder for top-down.
bool TopDownNestingDetected = false;
- for (BasicBlock *BB : llvm::reverse(PostOrder))
+ for (BasicBlock *BB : llvm::reverse(PostOrder)) {
TopDownNestingDetected |= VisitTopDown(BB, BBStates, Releases);
+ if (DisableRetainReleasePairing)
+ return false;
+ }
return TopDownNestingDetected && BottomUpNestingDetected;
}
// Analyze the CFG of the function, and all instructions.
bool NestingDetected = Visit(F, BBStates, Retains, Releases);
+ if (DisableRetainReleasePairing)
+ return false;
+
// Transform.
bool AnyPairsCompletelyEliminated = PerformCodePlacement(BBStates, Retains,
Releases,
--- /dev/null
+; RUN: opt -objc-arc -S < %s | FileCheck -check-prefix=ENABLE -check-prefix=CHECK %s
+; RUN: opt -objc-arc -arc-opt-max-ptr-states=1 -S < %s | FileCheck -check-prefix=DISABLE -check-prefix=CHECK %s
+
+@g0 = common global i8* null, align 8
+
+; CHECK: call i8* @llvm.objc.retain
+; ENABLE-NOT: call i8* @llvm.objc.retain
+; DISABLE: call i8* @llvm.objc.retain
+; CHECK: call void @llvm.objc.release
+; ENABLE-NOT: call void @llvm.objc.release
+; DISABLE: call void @llvm.objc.release
+
+define void @foo0(i8* %a) {
+ %1 = tail call i8* @llvm.objc.retain(i8* %a)
+ %2 = tail call i8* @llvm.objc.retain(i8* %a)
+ %3 = load i8*, i8** @g0, align 8
+ store i8* %a, i8** @g0, align 8
+ tail call void @llvm.objc.release(i8* %3)
+ tail call void @llvm.objc.release(i8* %a), !clang.imprecise_release !0
+ ret void
+}
+
+declare i8* @llvm.objc.retain(i8*)
+declare void @llvm.objc.release(i8*)
+
+!0 = !{}