do {
const Value *V = Worklist.pop_back_val();
- DEBUG(dbgs() << "DoesRetainableObjPtrEscape: Visiting: " << *V << "\n");
+ DEBUG(dbgs() << "Visiting: " << *V << "\n");
for (Value::const_use_iterator UI = V->use_begin(), UE = V->use_end();
UI != UE; ++UI) {
const User *UUser = *UI;
- DEBUG(dbgs() << "DoesRetainableObjPtrEscape: User: " << *UUser << "\n");
+ DEBUG(dbgs() << "User: " << *UUser << "\n");
// Special - Use by a call (callee or argument) is not considered
// to be an escape.
case IC_StoreStrong:
case IC_Autorelease:
case IC_AutoreleaseRV: {
- DEBUG(dbgs() << "DoesRetainableObjPtrEscape: User copies pointer "
- "arguments. Pointer Escapes!\n");
+ DEBUG(dbgs() << "User copies pointer arguments. Pointer Escapes!\n");
// These special functions make copies of their pointer arguments.
return true;
}
isa<PHINode>(UUser) || isa<SelectInst>(UUser)) {
if (VisitedSet.insert(UUser)) {
- DEBUG(dbgs() << "DoesRetainableObjPtrEscape: User copies value. "
- "Ptr escapes if result escapes. Adding to list.\n");
+ DEBUG(dbgs() << "User copies value. Ptr escapes if result escapes."
+ " Adding to list.\n");
Worklist.push_back(UUser);
} else {
- DEBUG(dbgs() << "DoesRetainableObjPtrEscape: Already visited node."
- "\n");
+ DEBUG(dbgs() << "Already visited node.\n");
}
continue;
}
continue;
}
// Otherwise, conservatively assume an escape.
- DEBUG(dbgs() << "DoesRetainableObjPtrEscape: Assuming ptr escapes.\n");
+ DEBUG(dbgs() << "Assuming ptr escapes.\n");
return true;
}
} while (!Worklist.empty());
// No escapes found.
- DEBUG(dbgs() << "DoesRetainableObjPtrEscape: Ptr does not escape.\n");
+ DEBUG(dbgs() << "Ptr does not escape.\n");
return false;
}
namespace {
/// \brief Unidirectional information about either a
/// retain-decrement-use-release sequence or release-use-decrement-retain
- /// reverese sequence.
+ /// reverse sequence.
struct RRInfo {
/// After an objc_retain, the reference count of the referenced
/// object is known to be positive. Similarly, before an objc_release, the
KnownSafe(false), IsTailCallRelease(false), ReleaseMetadata(0) {}
void clear();
+
+ bool IsTrackingImpreciseReleases() {
+ return ReleaseMetadata != 0;
+ }
};
}
/// True if the reference count is known to be incremented.
bool KnownPositiveRefCount;
- /// True of we've seen an opportunity for partial RR elimination, such as
+ /// True if we've seen an opportunity for partial RR elimination, such as
/// pushing calls into a CFG triangle or into one side of a CFG diamond.
bool Partial;
}
void SetSeq(Sequence NewSeq) {
+ DEBUG(dbgs() << "Old: " << Seq << "; New: " << NewSeq << "\n");
Seq = NewSeq;
}
}
void ResetSequenceProgress(Sequence NewSeq) {
- Seq = NewSeq;
+ DEBUG(dbgs() << "Resetting sequence progress.\n");
+ SetSeq(NewSeq);
Partial = false;
RRI.clear();
}
PtrState::Merge(const PtrState &Other, bool TopDown) {
Seq = MergeSeqs(Seq, Other.Seq, TopDown);
KnownPositiveRefCount = KnownPositiveRefCount && Other.KnownPositiveRefCount;
-
+
// If we're not in a sequence (anymore), drop all associated state.
if (Seq == S_None) {
Partial = false;
/// Enable/disable ARC sequence annotations.
static cl::opt<bool>
-EnableARCAnnotations("enable-objc-arc-annotations", cl::init(false));
+EnableARCAnnotations("enable-objc-arc-annotations", cl::init(false),
+ cl::desc("Enable emission of arc data flow analysis "
+ "annotations"));
+static cl::opt<bool>
+DisableCheckForCFGHazards("disable-objc-arc-checkforcfghazards", cl::init(false),
+ cl::desc("Disable check for cfg hazards when "
+ "annotating"));
+static cl::opt<std::string>
+ARCAnnotationTargetIdentifier("objc-arc-annotation-target-identifier",
+ cl::init(""),
+ cl::desc("filter out all data flow annotations "
+ "but those that apply to the given "
+ "target llvm identifier."));
/// This function appends a unique ARCAnnotationProvenanceSourceMDKind id to an
/// instruction so that we can track backwards when post processing via the llvm
/// state of a pointer at the entrance to a basic block.
static void GenerateARCBBEntranceAnnotation(const char *Name, BasicBlock *BB,
Value *Ptr, Sequence Seq) {
+ // If we have a target identifier, make sure that we match it before
+ // continuing.
+ if(!ARCAnnotationTargetIdentifier.empty() &&
+ !Ptr->getName().equals(ARCAnnotationTargetIdentifier))
+ return;
+
Module *M = BB->getParent()->getParent();
LLVMContext &C = M->getContext();
Type *I8X = PointerType::getUnqual(Type::getInt8Ty(C));
ArrayRef<Type*>(Params, 2),
/*isVarArg=*/false);
Constant *Callee = M->getOrInsertFunction(Name, FTy);
-
- IRBuilder<> Builder(BB, BB->getFirstInsertionPt());
-
+
+ IRBuilder<> Builder(BB, BB->getFirstInsertionPt());
+
Value *PtrName;
StringRef Tmp = Ptr->getName();
if (0 == (PtrName = M->getGlobalVariable(Tmp, true))) {
Value *ActualPtrName = Builder.CreateGlobalStringPtr(Tmp,
Tmp + "_STR");
PtrName = new GlobalVariable(*M, I8X, true, GlobalVariable::InternalLinkage,
- cast<Constant>(ActualPtrName), Tmp);
+ cast<Constant>(ActualPtrName), Tmp);
}
Value *S;
/// of the pointer at the bottom of the basic block.
static void GenerateARCBBTerminatorAnnotation(const char *Name, BasicBlock *BB,
Value *Ptr, Sequence Seq) {
+ // If we have a target identifier, make sure that we match it before emitting
+ // an annotation.
+ if(!ARCAnnotationTargetIdentifier.empty() &&
+ !Ptr->getName().equals(ARCAnnotationTargetIdentifier))
+ return;
+
Module *M = BB->getParent()->getParent();
LLVMContext &C = M->getContext();
Type *I8X = PointerType::getUnqual(Type::getInt8Ty(C));
ArrayRef<Type*>(Params, 2),
/*isVarArg=*/false);
Constant *Callee = M->getOrInsertFunction(Name, FTy);
-
- IRBuilder<> Builder(BB, llvm::prior(BB->end()));
-
+
+ IRBuilder<> Builder(BB, llvm::prior(BB->end()));
+
Value *PtrName;
StringRef Tmp = Ptr->getName();
if (0 == (PtrName = M->getGlobalVariable(Tmp, true))) {
Value *ActualPtrName = Builder.CreateGlobalStringPtr(Tmp,
Tmp + "_STR");
PtrName = new GlobalVariable(*M, I8X, true, GlobalVariable::InternalLinkage,
- cast<Constant>(ActualPtrName), Tmp);
+ cast<Constant>(ActualPtrName), Tmp);
}
Value *S;
S = new GlobalVariable(*M, I8X, true, GlobalVariable::InternalLinkage,
cast<Constant>(ActualPtrName), SeqStr);
}
- Builder.CreateCall2(Callee, PtrName, S);
+ Builder.CreateCall2(Callee, PtrName, S);
}
/// Adds a source annotation to pointer and a state change annotation to Inst
Sequence OldSeq,
Sequence NewSeq) {
if (EnableARCAnnotations) {
+ // If we have a target identifier, make sure that we match it before
+ // emitting an annotation.
+ if(!ARCAnnotationTargetIdentifier.empty() &&
+ !Ptr->getName().equals(ARCAnnotationTargetIdentifier))
+ return;
+
// First generate the source annotation on our pointer. This will return an
// MDString* if Ptr actually comes from an instruction implying we can put
// in a source annotation. If AppendMDNodeToSourcePtr returns 0 (i.e. NULL),
ARCAnnotationProvenanceSourceMDKind, (inst), \
const_cast<Value*>(ptr), (old), (new))
+#define ANNOTATE_BB(_states, _bb, _name, _type, _direction) \
+ do { \
+ if (EnableARCAnnotations) { \
+ for(BBState::ptr_const_iterator I = (_states)._direction##_ptr_begin(), \
+ E = (_states)._direction##_ptr_end(); I != E; ++I) { \
+ Value *Ptr = const_cast<Value*>(I->first); \
+ Sequence Seq = I->second.GetSeq(); \
+ GenerateARCBB ## _type ## Annotation(_name, (_bb), Ptr, Seq); \
+ } \
+ } \
+ } while (0)
+
+#define ANNOTATE_BOTTOMUP_BBSTART(_states, _basicblock) \
+ ANNOTATE_BB(_states, _basicblock, "llvm.arc.annotation.bottomup.bbstart", \
+ Entrance, bottom_up)
+#define ANNOTATE_BOTTOMUP_BBEND(_states, _basicblock) \
+ ANNOTATE_BB(_states, _basicblock, "llvm.arc.annotation.bottomup.bbend", \
+ Terminator, bottom_up)
+#define ANNOTATE_TOPDOWN_BBSTART(_states, _basicblock) \
+ ANNOTATE_BB(_states, _basicblock, "llvm.arc.annotation.topdown.bbstart", \
+ Entrance, top_down)
+#define ANNOTATE_TOPDOWN_BBEND(_states, _basicblock) \
+ ANNOTATE_BB(_states, _basicblock, "llvm.arc.annotation.topdown.bbend", \
+ Terminator, top_down)
+
#else // !ARC_ANNOTATION
// If annotations are off, noop.
#define ANNOTATE_BOTTOMUP(inst, ptr, old, new)
#define ANNOTATE_TOPDOWN(inst, ptr, old, new)
+#define ANNOTATE_BOTTOMUP_BBSTART(states, basicblock)
+#define ANNOTATE_BOTTOMUP_BBEND(states, basicblock)
+#define ANNOTATE_TOPDOWN_BBSTART(states, basicblock)
+#define ANNOTATE_TOPDOWN_BBEND(states, basicblock)
#endif // !ARC_ANNOTATION
namespace {
Changed = true;
++NumPeeps;
- DEBUG(dbgs() << "ObjCARCOpt::OptimizeRetainCall: Transforming "
- "objc_retain => objc_retainAutoreleasedReturnValue"
- " since the operand is a return value.\n"
- " Old: "
- << *Retain << "\n");
+ DEBUG(dbgs() << "Transforming objc_retain => "
+ "objc_retainAutoreleasedReturnValue since the operand is a "
+ "return value.\nOld: "<< *Retain << "\n");
cast<CallInst>(Retain)->setCalledFunction(getRetainRVCallee(F.getParent()));
- DEBUG(dbgs() << " New: "
- << *Retain << "\n");
+ DEBUG(dbgs() << "New: " << *Retain << "\n");
}
/// Turn objc_retainAutoreleasedReturnValue into objc_retain if the operand is
Changed = true;
++NumPeeps;
- DEBUG(dbgs() << "ObjCARCOpt::OptimizeRetainRVCall: Erasing " << *I << "\n"
- << " Erasing " << *RetainRV
- << "\n");
+ DEBUG(dbgs() << "Erasing autoreleaseRV,retainRV pair: " << *I << "\n"
+ << "Erasing " << *RetainRV << "\n");
EraseInstruction(I);
EraseInstruction(RetainRV);
Changed = true;
++NumPeeps;
- DEBUG(dbgs() << "ObjCARCOpt::OptimizeRetainRVCall: Transforming "
- "objc_retainAutoreleasedReturnValue => "
+ DEBUG(dbgs() << "Transforming objc_retainAutoreleasedReturnValue => "
"objc_retain since the operand is not a return value.\n"
- " Old: "
- << *RetainRV << "\n");
+ "Old = " << *RetainRV << "\n");
cast<CallInst>(RetainRV)->setCalledFunction(getRetainCallee(F.getParent()));
- DEBUG(dbgs() << " New: "
- << *RetainRV << "\n");
+ DEBUG(dbgs() << "New = " << *RetainRV << "\n");
return false;
}
Changed = true;
++NumPeeps;
- DEBUG(dbgs() << "ObjCARCOpt::OptimizeAutoreleaseRVCall: Transforming "
- "objc_autoreleaseReturnValue => "
+ DEBUG(dbgs() << "Transforming objc_autoreleaseReturnValue => "
"objc_autorelease since its operand is not used as a return "
"value.\n"
- " Old: "
- << *AutoreleaseRV << "\n");
+ "Old = " << *AutoreleaseRV << "\n");
CallInst *AutoreleaseRVCI = cast<CallInst>(AutoreleaseRV);
AutoreleaseRVCI->
AutoreleaseRVCI->setTailCall(false); // Never tail call objc_autorelease.
Class = IC_Autorelease;
- DEBUG(dbgs() << " New: "
- << *AutoreleaseRV << "\n");
+ DEBUG(dbgs() << "New: " << *AutoreleaseRV << "\n");
}
InstructionClass &Class) {
assert(GetBasicInstructionClass(Inst) == Class);
assert(IC_RetainBlock == Class);
-
+
// If we can not optimize Inst, return false.
if (!IsRetainBlockOptimizable(Inst))
return false;
-
+
+ Changed = true;
+ ++NumPeeps;
+
+ DEBUG(dbgs() << "Strength reduced retainBlock => retain.\n");
+ DEBUG(dbgs() << "Old: " << *Inst << "\n");
CallInst *RetainBlock = cast<CallInst>(Inst);
RetainBlock->setCalledFunction(getRetainCallee(F.getParent()));
// Remove copy_on_escape metadata.
RetainBlock->setMetadata(CopyOnEscapeMDKind, 0);
Class = IC_Retain;
-
+ DEBUG(dbgs() << "New: " << *Inst << "\n");
return true;
}
/// Visit each call, one at a time, and make simplifications without doing any
/// additional analysis.
void ObjCARCOpt::OptimizeIndividualCalls(Function &F) {
+ DEBUG(dbgs() << "\n== ObjCARCOpt::OptimizeIndividualCalls ==\n");
// Reset all the flags in preparation for recomputing them.
UsedInThisFunction = 0;
InstructionClass Class = GetBasicInstructionClass(Inst);
- DEBUG(dbgs() << "ObjCARCOpt::OptimizeIndividualCalls: Visiting: Class: "
- << Class << "; " << *Inst << "\n");
+ DEBUG(dbgs() << "Visiting: Class: " << Class << "; " << *Inst << "\n");
switch (Class) {
default: break;
case IC_NoopCast:
Changed = true;
++NumNoops;
- DEBUG(dbgs() << "ObjCARCOpt::OptimizeIndividualCalls: Erasing no-op cast:"
- " " << *Inst << "\n");
+ DEBUG(dbgs() << "Erasing no-op cast: " << *Inst << "\n");
EraseInstruction(Inst);
continue;
Constant::getNullValue(Ty),
CI);
llvm::Value *NewValue = UndefValue::get(CI->getType());
- DEBUG(dbgs() << "ObjCARCOpt::OptimizeIndividualCalls: A null "
- "pointer-to-weak-pointer is undefined behavior.\n"
- " Old = " << *CI <<
- "\n New = " <<
- *NewValue << "\n");
+ DEBUG(dbgs() << "A null pointer-to-weak-pointer is undefined behavior."
+ "\nOld = " << *CI << "\nNew = " << *NewValue << "\n");
CI->replaceAllUsesWith(NewValue);
CI->eraseFromParent();
continue;
CI);
llvm::Value *NewValue = UndefValue::get(CI->getType());
- DEBUG(dbgs() << "ObjCARCOpt::OptimizeIndividualCalls: A null "
- "pointer-to-weak-pointer is undefined behavior.\n"
- " Old = " << *CI <<
- "\n New = " <<
- *NewValue << "\n");
+ DEBUG(dbgs() << "A null pointer-to-weak-pointer is undefined behavior."
+ "\nOld = " << *CI << "\nNew = " << *NewValue << "\n");
CI->replaceAllUsesWith(NewValue);
CI->eraseFromParent();
break;
}
case IC_RetainBlock:
- // If we strength reduce an objc_retainBlock to amn objc_retain, continue
+ // If we strength reduce an objc_retainBlock to an objc_retain, continue
// onto the objc_retain peephole optimizations. Otherwise break.
if (!OptimizeRetainBlockCall(F, Inst, Class))
break;
NewCall->setMetadata(ImpreciseReleaseMDKind,
MDNode::get(C, ArrayRef<Value *>()));
- DEBUG(dbgs() << "ObjCARCOpt::OptimizeIndividualCalls: Replacing "
- "objc_autorelease(x) with objc_release(x) since x is "
- "otherwise unused.\n"
- " Old: " << *Call <<
- "\n New: " <<
- *NewCall << "\n");
+ DEBUG(dbgs() << "Replacing autorelease{,RV}(x) with objc_release(x) "
+ "since x is otherwise unused.\nOld: " << *Call << "\nNew: "
+ << *NewCall << "\n");
EraseInstruction(Call);
Inst = NewCall;
// a tail keyword.
if (IsAlwaysTail(Class)) {
Changed = true;
- DEBUG(dbgs() << "ObjCARCOpt::OptimizeIndividualCalls: Adding tail keyword"
- " to function since it can never be passed stack args: " << *Inst <<
- "\n");
+ DEBUG(dbgs() << "Adding tail keyword to function since it can never be "
+ "passed stack args: " << *Inst << "\n");
cast<CallInst>(Inst)->setTailCall();
}
// semantics of ARC truly do not do so.
if (IsNeverTail(Class)) {
Changed = true;
- DEBUG(dbgs() << "ObjCARCOpt::OptimizeIndividualCalls: Removing tail "
- "keyword from function: " << *Inst <<
+ DEBUG(dbgs() << "Removing tail keyword from function: " << *Inst <<
"\n");
cast<CallInst>(Inst)->setTailCall(false);
}
// Set nounwind as needed.
if (IsNoThrow(Class)) {
Changed = true;
- DEBUG(dbgs() << "ObjCARCOpt::OptimizeIndividualCalls: Found no throw"
- " class. Setting nounwind on: " << *Inst << "\n");
+ DEBUG(dbgs() << "Found no throw class. Setting nounwind on: " << *Inst
+ << "\n");
cast<CallInst>(Inst)->setDoesNotThrow();
}
if (IsNullOrUndef(Arg)) {
Changed = true;
++NumNoops;
- DEBUG(dbgs() << "ObjCARCOpt::OptimizeIndividualCalls: ARC calls with "
- " null are no-ops. Erasing: " << *Inst << "\n");
+ DEBUG(dbgs() << "ARC calls with null are no-ops. Erasing: " << *Inst
+ << "\n");
EraseInstruction(Inst);
continue;
}
Clone->setArgOperand(0, Op);
Clone->insertBefore(InsertPos);
- DEBUG(dbgs() << "ObjCARCOpt::OptimizeIndividualCalls: Cloning "
+ DEBUG(dbgs() << "Cloning "
<< *CInst << "\n"
- " And inserting "
- "clone at " << *InsertPos << "\n");
+ "And inserting clone at " << *InsertPos << "\n");
Worklist.push_back(std::make_pair(Clone, Incoming));
}
}
}
} while (!Worklist.empty());
}
- DEBUG(dbgs() << "ObjCARCOpt::OptimizeIndividualCalls: Finished List.\n");
+}
+
+/// If we have a top down pointer in the S_Use state, make sure that there are
+/// no CFG hazards by checking the states of various bottom up pointers.
+static void CheckForUseCFGHazard(const Sequence SuccSSeq,
+ const bool SuccSRRIKnownSafe,
+ PtrState &S,
+ bool &SomeSuccHasSame,
+ bool &AllSuccsHaveSame,
+ bool &ShouldContinue) {
+ switch (SuccSSeq) {
+ case S_CanRelease: {
+ if (!S.RRI.KnownSafe && !SuccSRRIKnownSafe) {
+ S.ClearSequenceProgress();
+ break;
+ }
+ ShouldContinue = true;
+ break;
+ }
+ case S_Use:
+ SomeSuccHasSame = true;
+ break;
+ case S_Stop:
+ case S_Release:
+ case S_MovableRelease:
+ if (!S.RRI.KnownSafe && !SuccSRRIKnownSafe)
+ AllSuccsHaveSame = false;
+ break;
+ case S_Retain:
+ llvm_unreachable("bottom-up pointer in retain state!");
+ case S_None:
+ llvm_unreachable("This should have been handled earlier.");
+ }
+}
+
+/// If we have a Top Down pointer in the S_CanRelease state, make sure that
+/// there are no CFG hazards by checking the states of various bottom up
+/// pointers.
+static void CheckForCanReleaseCFGHazard(const Sequence SuccSSeq,
+ const bool SuccSRRIKnownSafe,
+ PtrState &S,
+ bool &SomeSuccHasSame,
+ bool &AllSuccsHaveSame) {
+ switch (SuccSSeq) {
+ case S_CanRelease:
+ SomeSuccHasSame = true;
+ break;
+ case S_Stop:
+ case S_Release:
+ case S_MovableRelease:
+ case S_Use:
+ if (!S.RRI.KnownSafe && !SuccSRRIKnownSafe)
+ AllSuccsHaveSame = false;
+ break;
+ case S_Retain:
+ llvm_unreachable("bottom-up pointer in retain state!");
+ case S_None:
+ llvm_unreachable("This should have been handled earlier.");
+ }
}
/// Check for critical edges, loop boundaries, irreducible control flow, or
// If any top-down local-use or possible-dec has a succ which is earlier in
// the sequence, forget it.
for (BBState::ptr_iterator I = MyStates.top_down_ptr_begin(),
- E = MyStates.top_down_ptr_end(); I != E; ++I)
- switch (I->second.GetSeq()) {
- default: break;
- case S_Use: {
- const Value *Arg = I->first;
- const TerminatorInst *TI = cast<TerminatorInst>(&BB->back());
- bool SomeSuccHasSame = false;
- bool AllSuccsHaveSame = true;
- PtrState &S = I->second;
- succ_const_iterator SI(TI), SE(TI, false);
-
- for (; SI != SE; ++SI) {
- Sequence SuccSSeq = S_None;
- bool SuccSRRIKnownSafe = false;
- // If VisitBottomUp has pointer information for this successor, take
- // what we know about it.
- DenseMap<const BasicBlock *, BBState>::iterator BBI =
- BBStates.find(*SI);
- assert(BBI != BBStates.end());
- const PtrState &SuccS = BBI->second.getPtrBottomUpState(Arg);
- SuccSSeq = SuccS.GetSeq();
- SuccSRRIKnownSafe = SuccS.RRI.KnownSafe;
- switch (SuccSSeq) {
- case S_None:
- case S_CanRelease: {
- if (!S.RRI.KnownSafe && !SuccSRRIKnownSafe) {
- S.ClearSequenceProgress();
- break;
- }
- continue;
- }
- case S_Use:
- SomeSuccHasSame = true;
- break;
- case S_Stop:
- case S_Release:
- case S_MovableRelease:
- if (!S.RRI.KnownSafe && !SuccSRRIKnownSafe)
- AllSuccsHaveSame = false;
- break;
- case S_Retain:
- llvm_unreachable("bottom-up pointer in retain state!");
- }
- }
- // If the state at the other end of any of the successor edges
- // matches the current state, require all edges to match. This
- // guards against loops in the middle of a sequence.
- if (SomeSuccHasSame && !AllSuccsHaveSame)
+ E = MyStates.top_down_ptr_end(); I != E; ++I) {
+ PtrState &S = I->second;
+ const Sequence Seq = I->second.GetSeq();
+
+ // We only care about S_Retain, S_CanRelease, and S_Use.
+ if (Seq == S_None)
+ continue;
+
+ // Make sure that if extra top down states are added in the future that this
+ // code is updated to handle it.
+ assert((Seq == S_Retain || Seq == S_CanRelease || Seq == S_Use) &&
+ "Unknown top down sequence state.");
+
+ const Value *Arg = I->first;
+ const TerminatorInst *TI = cast<TerminatorInst>(&BB->back());
+ bool SomeSuccHasSame = false;
+ bool AllSuccsHaveSame = true;
+
+ succ_const_iterator SI(TI), SE(TI, false);
+
+ for (; SI != SE; ++SI) {
+ // If VisitBottomUp has pointer information for this successor, take
+ // what we know about it.
+ const DenseMap<const BasicBlock *, BBState>::iterator BBI =
+ BBStates.find(*SI);
+ assert(BBI != BBStates.end());
+ const PtrState &SuccS = BBI->second.getPtrBottomUpState(Arg);
+ const Sequence SuccSSeq = SuccS.GetSeq();
+
+ // If bottom up, the pointer is in an S_None state, clear the sequence
+ // progress since the sequence in the bottom up state finished
+ // suggesting a mismatch in between retains/releases. This is true for
+ // all three cases that we are handling here: S_Retain, S_Use, and
+ // S_CanRelease.
+ if (SuccSSeq == S_None) {
S.ClearSequenceProgress();
- break;
- }
- case S_CanRelease: {
- const Value *Arg = I->first;
- const TerminatorInst *TI = cast<TerminatorInst>(&BB->back());
- bool SomeSuccHasSame = false;
- bool AllSuccsHaveSame = true;
- PtrState &S = I->second;
- succ_const_iterator SI(TI), SE(TI, false);
-
- for (; SI != SE; ++SI) {
- Sequence SuccSSeq = S_None;
- bool SuccSRRIKnownSafe = false;
- // If VisitBottomUp has pointer information for this successor, take
- // what we know about it.
- DenseMap<const BasicBlock *, BBState>::iterator BBI =
- BBStates.find(*SI);
- assert(BBI != BBStates.end());
- const PtrState &SuccS = BBI->second.getPtrBottomUpState(Arg);
- SuccSSeq = SuccS.GetSeq();
- SuccSRRIKnownSafe = SuccS.RRI.KnownSafe;
- switch (SuccSSeq) {
- case S_None: {
- if (!S.RRI.KnownSafe && !SuccSRRIKnownSafe) {
- S.ClearSequenceProgress();
- break;
- }
+ continue;
+ }
+
+ // If we have S_Use or S_CanRelease, perform our check for cfg hazard
+ // checks.
+ const bool SuccSRRIKnownSafe = SuccS.RRI.KnownSafe;
+
+ // *NOTE* We do not use Seq from above here since we are allowing for
+ // S.GetSeq() to change while we are visiting basic blocks.
+ switch(S.GetSeq()) {
+ case S_Use: {
+ bool ShouldContinue = false;
+ CheckForUseCFGHazard(SuccSSeq, SuccSRRIKnownSafe, S,
+ SomeSuccHasSame, AllSuccsHaveSame,
+ ShouldContinue);
+ if (ShouldContinue)
continue;
- }
- case S_CanRelease:
- SomeSuccHasSame = true;
- break;
- case S_Stop:
- case S_Release:
- case S_MovableRelease:
- case S_Use:
- if (!S.RRI.KnownSafe && !SuccSRRIKnownSafe)
- AllSuccsHaveSame = false;
- break;
- case S_Retain:
- llvm_unreachable("bottom-up pointer in retain state!");
- }
+ break;
+ }
+ case S_CanRelease: {
+ CheckForCanReleaseCFGHazard(SuccSSeq, SuccSRRIKnownSafe,
+ S, SomeSuccHasSame,
+ AllSuccsHaveSame);
+ break;
+ }
+ case S_Retain:
+ case S_None:
+ case S_Stop:
+ case S_Release:
+ case S_MovableRelease:
+ break;
}
- // If the state at the other end of any of the successor edges
- // matches the current state, require all edges to match. This
- // guards against loops in the middle of a sequence.
- if (SomeSuccHasSame && !AllSuccsHaveSame)
- S.ClearSequenceProgress();
- break;
- }
}
+
+ // If the state at the other end of any of the successor edges
+ // matches the current state, require all edges to match. This
+ // guards against loops in the middle of a sequence.
+ if (SomeSuccHasSame && !AllSuccsHaveSame)
+ S.ClearSequenceProgress();
+ }
}
bool
InstructionClass Class = GetInstructionClass(Inst);
const Value *Arg = 0;
+ DEBUG(dbgs() << "Class: " << Class << "\n");
+
switch (Class) {
case IC_Release: {
Arg = GetObjCArg(Inst);
// pairs by making PtrState hold a stack of states, but this is
// simple and avoids adding overhead for the non-nested case.
if (S.GetSeq() == S_Release || S.GetSeq() == S_MovableRelease) {
- DEBUG(dbgs() << "ObjCARCOpt::VisitInstructionBottomUp: Found nested "
- "releases (i.e. a release pair)\n");
+ DEBUG(dbgs() << "Found nested releases (i.e. a release pair)\n");
NestingDetected = true;
}
case S_Release:
case S_MovableRelease:
case S_Use:
- S.RRI.ReverseInsertPts.clear();
+ // If OldSeq is not S_Use or OldSeq is S_Use and we are tracking an
+ // imprecise release, clear our reverse insertion points.
+ if (OldSeq != S_Use || S.RRI.IsTrackingImpreciseReleases())
+ S.RRI.ReverseInsertPts.clear();
// FALL THROUGH
case S_CanRelease:
// Don't do retain+release tracking for IC_RetainRV, because it's
llvm_unreachable("bottom-up pointer in retain state!");
}
ANNOTATE_BOTTOMUP(Inst, Arg, OldSeq, S.GetSeq());
- return NestingDetected;
+ // A retain moving bottom up can be a use.
+ break;
}
case IC_AutoreleasepoolPop:
// Conservatively, clear MyStates for all known pointers.
// Check for possible releases.
if (CanAlterRefCount(Inst, Ptr, PA, Class)) {
+ DEBUG(dbgs() << "CanAlterRefCount: Seq: " << Seq << "; " << *Ptr
+ << "\n");
S.ClearKnownPositiveRefCount();
switch (Seq) {
case S_Use:
case S_Release:
case S_MovableRelease:
if (CanUse(Inst, Ptr, PA, Class)) {
+ DEBUG(dbgs() << "CanUse: Seq: " << Seq << "; " << *Ptr
+ << "\n");
assert(S.RRI.ReverseInsertPts.empty());
// If this is an invoke instruction, we're scanning it as part of
// one of its successor blocks, since we can't insert code after it
S.SetSeq(S_Use);
ANNOTATE_BOTTOMUP(Inst, Ptr, Seq, S_Use);
} else if (Seq == S_Release && IsUser(Class)) {
+ DEBUG(dbgs() << "PreciseReleaseUse: Seq: " << Seq << "; " << *Ptr
+ << "\n");
// Non-movable releases depend on any possible objc pointer use.
S.SetSeq(S_Stop);
ANNOTATE_BOTTOMUP(Inst, Ptr, S_Release, S_Stop);
break;
case S_Stop:
if (CanUse(Inst, Ptr, PA, Class)) {
+ DEBUG(dbgs() << "PreciseStopUse: Seq: " << Seq << "; " << *Ptr
+ << "\n");
S.SetSeq(S_Use);
ANNOTATE_BOTTOMUP(Inst, Ptr, Seq, S_Use);
}
ObjCARCOpt::VisitBottomUp(BasicBlock *BB,
DenseMap<const BasicBlock *, BBState> &BBStates,
MapVector<Value *, RRInfo> &Retains) {
+
+ DEBUG(dbgs() << "\n== ObjCARCOpt::VisitBottomUp ==\n");
+
bool NestingDetected = false;
BBState &MyStates = BBStates[BB];
assert(I != BBStates.end());
MyStates.MergeSucc(I->second);
}
- }
-
-#ifdef ARC_ANNOTATIONS
- if (EnableARCAnnotations) {
- // If ARC Annotations are enabled, output the current state of pointers at the
- // bottom of the basic block.
- for(BBState::ptr_const_iterator I = MyStates.bottom_up_ptr_begin(),
- E = MyStates.bottom_up_ptr_end(); I != E; ++I) {
- Value *Ptr = const_cast<Value*>(I->first);
- Sequence Seq = I->second.GetSeq();
- GenerateARCBBTerminatorAnnotation("llvm.arc.annotation.bottomup.bbend",
- BB, Ptr, Seq);
- }
}
-#endif
+ // If ARC Annotations are enabled, output the current state of pointers at the
+ // bottom of the basic block.
+ ANNOTATE_BOTTOMUP_BBEND(MyStates, BB);
// Visit all the instructions, bottom-up.
for (BasicBlock::iterator I = BB->end(), E = BB->begin(); I != E; --I) {
if (isa<InvokeInst>(Inst))
continue;
- DEBUG(dbgs() << "ObjCARCOpt::VisitButtonUp: Visiting " << *Inst << "\n");
+ DEBUG(dbgs() << "Visiting " << *Inst << "\n");
NestingDetected |= VisitInstructionBottomUp(Inst, BB, Retains, MyStates);
}
NestingDetected |= VisitInstructionBottomUp(II, BB, Retains, MyStates);
}
-#ifdef ARC_ANNOTATIONS
- if (EnableARCAnnotations) {
- // If ARC Annotations are enabled, output the current state of pointers at the
- // top of the basic block.
- for(BBState::ptr_const_iterator I = MyStates.bottom_up_ptr_begin(),
- E = MyStates.bottom_up_ptr_end(); I != E; ++I) {
- Value *Ptr = const_cast<Value*>(I->first);
- Sequence Seq = I->second.GetSeq();
- GenerateARCBBEntranceAnnotation("llvm.arc.annotation.bottomup.bbstart",
- BB, Ptr, Seq);
- }
- }
-#endif
+ // If ARC Annotations are enabled, output the current state of pointers at the
+ // top of the basic block.
+ ANNOTATE_BOTTOMUP_BBSTART(MyStates, BB);
return NestingDetected;
}
PtrState &S = MyStates.getPtrTopDownState(Arg);
S.ClearKnownPositiveRefCount();
- switch (S.GetSeq()) {
+ Sequence OldSeq = S.GetSeq();
+
+ MDNode *ReleaseMetadata = Inst->getMetadata(ImpreciseReleaseMDKind);
+
+ switch (OldSeq) {
case S_Retain:
case S_CanRelease:
- S.RRI.ReverseInsertPts.clear();
+ if (OldSeq == S_Retain || ReleaseMetadata != 0)
+ S.RRI.ReverseInsertPts.clear();
// FALL THROUGH
case S_Use:
- S.RRI.ReleaseMetadata = Inst->getMetadata(ImpreciseReleaseMDKind);
+ S.RRI.ReleaseMetadata = ReleaseMetadata;
S.RRI.IsTailCallRelease = cast<CallInst>(Inst)->isTailCall();
Releases[Inst] = S.RRI;
ANNOTATE_TOPDOWN(Inst, Arg, S.GetSeq(), S_None);
// Check for possible releases.
if (CanAlterRefCount(Inst, Ptr, PA, Class)) {
+ DEBUG(dbgs() << "CanAlterRefCount: Seq: " << Seq << "; " << *Ptr
+ << "\n");
S.ClearKnownPositiveRefCount();
switch (Seq) {
case S_Retain:
switch (Seq) {
case S_CanRelease:
if (CanUse(Inst, Ptr, PA, Class)) {
+ DEBUG(dbgs() << "CanUse: Seq: " << Seq << "; " << *Ptr
+ << "\n");
S.SetSeq(S_Use);
ANNOTATE_TOPDOWN(Inst, Ptr, Seq, S_Use);
}
ObjCARCOpt::VisitTopDown(BasicBlock *BB,
DenseMap<const BasicBlock *, BBState> &BBStates,
DenseMap<Value *, RRInfo> &Releases) {
+ DEBUG(dbgs() << "\n== ObjCARCOpt::VisitTopDown ==\n");
bool NestingDetected = false;
BBState &MyStates = BBStates[BB];
}
}
-#ifdef ARC_ANNOTATIONS
- if (EnableARCAnnotations) {
- // If ARC Annotations are enabled, output the current state of pointers at the
- // top of the basic block.
- for(BBState::ptr_const_iterator I = MyStates.top_down_ptr_begin(),
- E = MyStates.top_down_ptr_end(); I != E; ++I) {
- Value *Ptr = const_cast<Value*>(I->first);
- Sequence Seq = I->second.GetSeq();
- GenerateARCBBEntranceAnnotation("llvm.arc.annotation.topdown.bbstart",
- BB, Ptr, Seq);
- }
- }
-#endif
+ // If ARC Annotations are enabled, output the current state of pointers at the
+ // top of the basic block.
+ ANNOTATE_TOPDOWN_BBSTART(MyStates, BB);
// Visit all the instructions, top-down.
for (BasicBlock::iterator I = BB->begin(), E = BB->end(); I != E; ++I) {
Instruction *Inst = I;
- DEBUG(dbgs() << "ObjCARCOpt::VisitTopDown: Visiting " << *Inst << "\n");
+ DEBUG(dbgs() << "Visiting " << *Inst << "\n");
NestingDetected |= VisitInstructionTopDown(Inst, Releases, MyStates);
}
+ // If ARC Annotations are enabled, output the current state of pointers at the
+ // bottom of the basic block.
+ ANNOTATE_TOPDOWN_BBEND(MyStates, BB);
+
#ifdef ARC_ANNOTATIONS
- if (EnableARCAnnotations) {
- // If ARC Annotations are enabled, output the current state of pointers at the
- // bottom of the basic block.
- for(BBState::ptr_const_iterator I = MyStates.top_down_ptr_begin(),
- E = MyStates.top_down_ptr_end(); I != E; ++I) {
- Value *Ptr = const_cast<Value*>(I->first);
- Sequence Seq = I->second.GetSeq();
- GenerateARCBBTerminatorAnnotation("llvm.arc.annotation.topdown.bbend",
- BB, Ptr, Seq);
- }
- }
+ if (!(EnableARCAnnotations && DisableCheckForCFGHazards))
#endif
-
CheckForCFGHazards(BB, BBStates, MyStates);
return NestingDetected;
}
Type *ArgTy = Arg->getType();
Type *ParamTy = PointerType::getUnqual(Type::getInt8Ty(ArgTy->getContext()));
+ DEBUG(dbgs() << "== ObjCARCOpt::MoveCalls ==\n");
+
// Insert the new retain and release calls.
for (SmallPtrSet<Instruction *, 2>::const_iterator
PI = ReleasesToMove.ReverseInsertPts.begin(),
CallInst::Create(getRetainCallee(M), MyArg, "", InsertPt);
Call->setDoesNotThrow();
Call->setTailCall();
-
- DEBUG(dbgs() << "ObjCARCOpt::MoveCalls: Inserting new Release: " << *Call
- << "\n"
- " At insertion point: " << *InsertPt
- << "\n");
+
+ DEBUG(dbgs() << "Inserting new Retain: " << *Call << "\n"
+ "At insertion point: " << *InsertPt << "\n");
}
for (SmallPtrSet<Instruction *, 2>::const_iterator
PI = RetainsToMove.ReverseInsertPts.begin(),
if (ReleasesToMove.IsTailCallRelease)
Call->setTailCall();
- DEBUG(dbgs() << "ObjCARCOpt::MoveCalls: Inserting new Retain: " << *Call
- << "\n"
- " At insertion point: " << *InsertPt
- << "\n");
+ DEBUG(dbgs() << "Inserting new Release: " << *Call << "\n"
+ "At insertion point: " << *InsertPt << "\n");
}
// Delete the original retain and release calls.
Instruction *OrigRetain = *AI;
Retains.blot(OrigRetain);
DeadInsts.push_back(OrigRetain);
- DEBUG(dbgs() << "ObjCARCOpt::MoveCalls: Deleting retain: " << *OrigRetain <<
- "\n");
+ DEBUG(dbgs() << "Deleting retain: " << *OrigRetain << "\n");
}
for (SmallPtrSet<Instruction *, 2>::const_iterator
AI = ReleasesToMove.Calls.begin(),
Instruction *OrigRelease = *AI;
Releases.erase(OrigRelease);
DeadInsts.push_back(OrigRelease);
- DEBUG(dbgs() << "ObjCARCOpt::MoveCalls: Deleting release: " << *OrigRelease
- << "\n");
+ DEBUG(dbgs() << "Deleting release: " << *OrigRelease << "\n");
}
+
}
bool
MapVector<Value *, RRInfo> &Retains,
DenseMap<Value *, RRInfo> &Releases,
Module *M) {
+ DEBUG(dbgs() << "\n== ObjCARCOpt::PerformCodePlacement ==\n");
+
bool AnyPairsCompletelyEliminated = false;
RRInfo RetainsToMove;
RRInfo ReleasesToMove;
Instruction *Retain = cast<Instruction>(V);
- DEBUG(dbgs() << "ObjCARCOpt::PerformCodePlacement: Visiting: " << *Retain
- << "\n");
+ DEBUG(dbgs() << "Visiting: " << *Retain << "\n");
Value *Arg = GetObjCArg(Retain);
/// Weak pointer optimizations.
void ObjCARCOpt::OptimizeWeakCalls(Function &F) {
+ DEBUG(dbgs() << "\n== ObjCARCOpt::OptimizeWeakCalls ==\n");
+
// First, do memdep-style RLE and S2L optimizations. We can't use memdep
// itself because it uses AliasAnalysis and we need to do provenance
// queries instead.
for (inst_iterator I = inst_begin(&F), E = inst_end(&F); I != E; ) {
Instruction *Inst = &*I++;
- DEBUG(dbgs() << "ObjCARCOpt::OptimizeWeakCalls: Visiting: " << *Inst <<
- "\n");
+ DEBUG(dbgs() << "Visiting: " << *Inst << "\n");
InstructionClass Class = GetBasicInstructionClass(Inst);
if (Class != IC_LoadWeak && Class != IC_LoadWeakRetained)
done:;
}
}
-
- DEBUG(dbgs() << "ObjCARCOpt::OptimizeWeakCalls: Finished List.\n\n");
-
}
/// Identify program paths which execute sequences of retains and releases which
NestingDetected;
}
+/// Check if there is a dependent call earlier that does not have anything in
+/// between the Retain and the call that can affect the reference count of their
+/// shared pointer argument. Note that Retain need not be in BB.
+static bool
+HasSafePathToPredecessorCall(const Value *Arg, Instruction *Retain,
+ SmallPtrSet<Instruction *, 4> &DepInsts,
+ SmallPtrSet<const BasicBlock *, 4> &Visited,
+ ProvenanceAnalysis &PA) {
+ FindDependencies(CanChangeRetainCount, Arg, Retain->getParent(), Retain,
+ DepInsts, Visited, PA);
+ if (DepInsts.size() != 1)
+ return false;
+
+ CallInst *Call =
+ dyn_cast_or_null<CallInst>(*DepInsts.begin());
+
+ // Check that the pointer is the return value of the call.
+ if (!Call || Arg != Call)
+ return false;
+
+ // Check that the call is a regular call.
+ InstructionClass Class = GetBasicInstructionClass(Call);
+ if (Class != IC_CallOrUser && Class != IC_Call)
+ return false;
+
+ return true;
+}
+
+/// Find a dependent retain that precedes the given autorelease for which there
+/// is nothing in between the two instructions that can affect the ref count of
+/// Arg.
+static CallInst *
+FindPredecessorRetainWithSafePath(const Value *Arg, BasicBlock *BB,
+ Instruction *Autorelease,
+ SmallPtrSet<Instruction *, 4> &DepInsts,
+ SmallPtrSet<const BasicBlock *, 4> &Visited,
+ ProvenanceAnalysis &PA) {
+ FindDependencies(CanChangeRetainCount, Arg,
+ BB, Autorelease, DepInsts, Visited, PA);
+ if (DepInsts.size() != 1)
+ return 0;
+
+ CallInst *Retain =
+ dyn_cast_or_null<CallInst>(*DepInsts.begin());
+
+ // Check that we found a retain with the same argument.
+ if (!Retain ||
+ !IsRetain(GetBasicInstructionClass(Retain)) ||
+ GetObjCArg(Retain) != Arg) {
+ return 0;
+ }
+
+ return Retain;
+}
+
+/// Look for an ``autorelease'' instruction dependent on Arg such that there are
+/// no instructions dependent on Arg that need a positive ref count in between
+/// the autorelease and the ret.
+static CallInst *
+FindPredecessorAutoreleaseWithSafePath(const Value *Arg, BasicBlock *BB,
+ ReturnInst *Ret,
+ SmallPtrSet<Instruction *, 4> &DepInsts,
+ SmallPtrSet<const BasicBlock *, 4> &V,
+ ProvenanceAnalysis &PA) {
+ FindDependencies(NeedsPositiveRetainCount, Arg,
+ BB, Ret, DepInsts, V, PA);
+ if (DepInsts.size() != 1)
+ return 0;
+
+ CallInst *Autorelease =
+ dyn_cast_or_null<CallInst>(*DepInsts.begin());
+ if (!Autorelease)
+ return 0;
+ InstructionClass AutoreleaseClass = GetBasicInstructionClass(Autorelease);
+ if (!IsAutorelease(AutoreleaseClass))
+ return 0;
+ if (GetObjCArg(Autorelease) != Arg)
+ return 0;
+
+ return Autorelease;
+}
+
/// Look for this pattern:
/// \code
/// %call = call i8* @something(...)
/// ret i8* %3
/// \endcode
/// And delete the retain and autorelease.
-///
-/// Otherwise if it's just this:
-/// \code
-/// %3 = call i8* @objc_autorelease(i8* %2)
-/// ret i8* %3
-/// \endcode
-/// convert the autorelease to autoreleaseRV.
void ObjCARCOpt::OptimizeReturns(Function &F) {
if (!F.getReturnType()->isPointerTy())
return;
+ DEBUG(dbgs() << "\n== ObjCARCOpt::OptimizeReturns ==\n");
+
SmallPtrSet<Instruction *, 4> DependingInstructions;
SmallPtrSet<const BasicBlock *, 4> Visited;
for (Function::iterator FI = F.begin(), FE = F.end(); FI != FE; ++FI) {
BasicBlock *BB = FI;
ReturnInst *Ret = dyn_cast<ReturnInst>(&BB->back());
- DEBUG(dbgs() << "ObjCARCOpt::OptimizeReturns: Visiting: " << *Ret << "\n");
+ DEBUG(dbgs() << "Visiting: " << *Ret << "\n");
- if (!Ret) continue;
+ if (!Ret)
+ continue;
const Value *Arg = StripPointerCastsAndObjCCalls(Ret->getOperand(0));
- FindDependencies(NeedsPositiveRetainCount, Arg,
- BB, Ret, DependingInstructions, Visited, PA);
- if (DependingInstructions.size() != 1)
- goto next_block;
-
- {
- CallInst *Autorelease =
- dyn_cast_or_null<CallInst>(*DependingInstructions.begin());
- if (!Autorelease)
- goto next_block;
- InstructionClass AutoreleaseClass = GetBasicInstructionClass(Autorelease);
- if (!IsAutorelease(AutoreleaseClass))
- goto next_block;
- if (GetObjCArg(Autorelease) != Arg)
- goto next_block;
-
- DependingInstructions.clear();
- Visited.clear();
-
- // Check that there is nothing that can affect the reference
- // count between the autorelease and the retain.
- FindDependencies(CanChangeRetainCount, Arg,
- BB, Autorelease, DependingInstructions, Visited, PA);
- if (DependingInstructions.size() != 1)
- goto next_block;
-
- {
- CallInst *Retain =
- dyn_cast_or_null<CallInst>(*DependingInstructions.begin());
-
- // Check that we found a retain with the same argument.
- if (!Retain ||
- !IsRetain(GetBasicInstructionClass(Retain)) ||
- GetObjCArg(Retain) != Arg)
- goto next_block;
-
- DependingInstructions.clear();
- Visited.clear();
-
- // Convert the autorelease to an autoreleaseRV, since it's
- // returning the value.
- if (AutoreleaseClass == IC_Autorelease) {
- DEBUG(dbgs() << "ObjCARCOpt::OptimizeReturns: Converting autorelease "
- "=> autoreleaseRV since it's returning a value.\n"
- " In: " << *Autorelease
- << "\n");
- Autorelease->setCalledFunction(getAutoreleaseRVCallee(F.getParent()));
- DEBUG(dbgs() << " Out: " << *Autorelease
- << "\n");
- Autorelease->setTailCall(); // Always tail call autoreleaseRV.
- AutoreleaseClass = IC_AutoreleaseRV;
- }
- // Check that there is nothing that can affect the reference
- // count between the retain and the call.
- // Note that Retain need not be in BB.
- FindDependencies(CanChangeRetainCount, Arg, Retain->getParent(), Retain,
- DependingInstructions, Visited, PA);
- if (DependingInstructions.size() != 1)
- goto next_block;
-
- {
- CallInst *Call =
- dyn_cast_or_null<CallInst>(*DependingInstructions.begin());
+ // Look for an ``autorelease'' instruction that is a predecessor of Ret and
+ // dependent on Arg such that there are no instructions dependent on Arg
+ // that need a positive ref count in between the autorelease and Ret.
+ CallInst *Autorelease =
+ FindPredecessorAutoreleaseWithSafePath(Arg, BB, Ret,
+ DependingInstructions, Visited,
+ PA);
+ DependingInstructions.clear();
+ Visited.clear();
- // Check that the pointer is the return value of the call.
- if (!Call || Arg != Call)
- goto next_block;
+ if (!Autorelease)
+ continue;
- // Check that the call is a regular call.
- InstructionClass Class = GetBasicInstructionClass(Call);
- if (Class != IC_CallOrUser && Class != IC_Call)
- goto next_block;
+ CallInst *Retain =
+ FindPredecessorRetainWithSafePath(Arg, BB, Autorelease,
+ DependingInstructions, Visited, PA);
+ DependingInstructions.clear();
+ Visited.clear();
- // If so, we can zap the retain and autorelease.
- Changed = true;
- ++NumRets;
- DEBUG(dbgs() << "ObjCARCOpt::OptimizeReturns: Erasing: " << *Retain
- << "\n Erasing: "
- << *Autorelease << "\n");
- EraseInstruction(Retain);
- EraseInstruction(Autorelease);
- }
- }
- }
+ if (!Retain)
+ continue;
- next_block:
+ // Check that there is nothing that can affect the reference count
+ // between the retain and the call. Note that Retain need not be in BB.
+ bool HasSafePathToCall = HasSafePathToPredecessorCall(Arg, Retain,
+ DependingInstructions,
+ Visited, PA);
DependingInstructions.clear();
Visited.clear();
- }
- DEBUG(dbgs() << "ObjCARCOpt::OptimizeReturns: Finished List.\n\n");
+ if (!HasSafePathToCall)
+ continue;
+ // If so, we can zap the retain and autorelease.
+ Changed = true;
+ ++NumRets;
+ DEBUG(dbgs() << "Erasing: " << *Retain << "\nErasing: "
+ << *Autorelease << "\n");
+ EraseInstruction(Retain);
+ EraseInstruction(Autorelease);
+ }
}
bool ObjCARCOpt::doInitialization(Module &M) {
Changed = false;
- DEBUG(dbgs() << "ObjCARCOpt: Visiting Function: " << F.getName() << "\n");
+ DEBUG(dbgs() << "<<< ObjCARCOpt: Visiting Function: " << F.getName() << " >>>"
+ "\n");
PA.setAA(&getAnalysis<AliasAnalysis>());