// IC_RetainBlock may be given a stack argument.
return Class == IC_Retain ||
Class == IC_RetainRV ||
- Class == IC_Autorelease ||
Class == IC_AutoreleaseRV;
}
+/// \brief Test if the given class represents instructions which are never safe
+/// to mark with the "tail" keyword.
+static bool IsNeverTail(InstructionClass Class) {
+ /// It is never safe to tail call objc_autorelease since by tail calling
+ /// objc_autorelease, we also tail call -[NSObject autorelease] which supports
+ /// fast autoreleasing causing our object to be potentially reclaimed from the
+ /// autorelease pool which violates the semantics of __autoreleasing types in
+ /// ARC.
+ return Class == IC_Autorelease;
+}
+
/// IsNoThrow - Test if the given class represents instructions which are always
/// safe to mark with the nounwind attribute..
static bool IsNoThrow(InstructionClass Class) {
for (inst_iterator I = inst_begin(&F), E = inst_end(&F); I != E; ++I) {
Instruction *Inst = &*I;
-
+
DEBUG(dbgs() << "ObjCARCExpand: Visiting: " << *Inst << "\n");
-
+
switch (GetBasicInstructionClass(Inst)) {
case IC_Retain:
case IC_RetainRV:
break;
}
}
-
+
DEBUG(dbgs() << "ObjCARCExpand: Finished List.\n\n");
-
+
return Changed;
}
// Turn it to an objc_retainAutoreleasedReturnValue..
Changed = true;
++NumPeeps;
-
+
DEBUG(dbgs() << "ObjCARCOpt::OptimizeRetainCall: Transforming "
"objc_retainAutoreleasedReturnValue => "
"objc_retain since the operand is not a return value.\n"
" Old: "
<< *Retain << "\n");
-
+
cast<CallInst>(Retain)->setCalledFunction(getRetainRVCallee(F.getParent()));
DEBUG(dbgs() << " New: "
GetObjCArg(I) == Arg) {
Changed = true;
++NumPeeps;
-
+
DEBUG(dbgs() << "ObjCARCOpt::OptimizeRetainRVCall: Erasing " << *I << "\n"
<< " Erasing " << *RetainRV
<< "\n");
-
+
EraseInstruction(I);
EraseInstruction(RetainRV);
return true;
// Turn it to a plain objc_retain.
Changed = true;
++NumPeeps;
-
+
DEBUG(dbgs() << "ObjCARCOpt::OptimizeRetainRVCall: Transforming "
"objc_retainAutoreleasedReturnValue => "
"objc_retain since the operand is not a return value.\n"
" Old: "
<< *RetainRV << "\n");
-
+
cast<CallInst>(RetainRV)->setCalledFunction(getRetainCallee(F.getParent()));
DEBUG(dbgs() << " New: "
" Old: "
<< *AutoreleaseRV << "\n");
- cast<CallInst>(AutoreleaseRV)->
+ CallInst *AutoreleaseRVCI = cast<CallInst>(AutoreleaseRV);
+ AutoreleaseRVCI->
setCalledFunction(getAutoreleaseCallee(F.getParent()));
-
+ AutoreleaseRVCI->setTailCall(false); // Never tail call objc_autorelease.
+
DEBUG(dbgs() << " New: "
<< *AutoreleaseRV << "\n");
-
+
}
/// OptimizeIndividualCalls - Visit each call, one at a time, and make
new StoreInst(UndefValue::get(cast<PointerType>(Ty)->getElementType()),
Constant::getNullValue(Ty),
CI);
- llvm::Value *NewValue = UndefValue::get(CI->getType());
+ 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");
+ *NewValue << "\n");
CI->replaceAllUsesWith(NewValue);
CI->eraseFromParent();
continue;
" Old = " << *CI <<
"\n New = " <<
*NewValue << "\n");
-
+
CI->replaceAllUsesWith(NewValue);
CI->eraseFromParent();
continue;
Call->getArgOperand(0), "", Call);
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");
-
+
EraseInstruction(Call);
Inst = NewCall;
Class = IC_Release;
cast<CallInst>(Inst)->setTailCall();
}
+ // Ensure that functions that can never have a "tail" keyword due to the
+ // semantics of ARC truly do not do so.
+ if (IsNeverTail(Class)) {
+ Changed = true;
+ DEBUG(dbgs() << "ObjCARCOpt::OptimizeIndividualCalls: 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");
cast<CallInst>(Inst)->setDoesNotThrow();
}
if (isNullOrUndef(Arg)) {
Changed = true;
++NumNoops;
+ DEBUG(dbgs() << "ObjCARCOpt::OptimizeIndividualCalls: ARC calls with "
+ " null are no-ops. Erasing: " << *Inst << "\n");
EraseInstruction(Inst);
continue;
}
Op = new BitCastInst(Op, ParamTy, "", InsertPos);
Clone->setArgOperand(0, Op);
Clone->insertBefore(InsertPos);
+
+ DEBUG(dbgs() << "ObjCARCOpt::OptimizeIndividualCalls: Cloning "
+ << *CInst << "\n"
+ " And inserting "
+ "clone at " << *InsertPos << "\n");
Worklist.push_back(std::make_pair(Clone, Incoming));
}
}
// Erase the original call.
+ DEBUG(dbgs() << "Erasing: " << *CInst << "\n");
EraseInstruction(CInst);
continue;
}
MDNode::get(M->getContext(), ArrayRef<Value *>()));
else
Call->setTailCall();
+
+ DEBUG(dbgs() << "ObjCARCOpt::MoveCalls: Inserting new Release: " << *Call
+ << "\n"
+ " At insertion point: " << *InsertPt
+ << "\n");
}
for (SmallPtrSet<Instruction *, 2>::const_iterator
PI = RetainsToMove.ReverseInsertPts.begin(),
Call->setDoesNotThrow();
if (ReleasesToMove.IsTailCallRelease)
Call->setTailCall();
+
+ DEBUG(dbgs() << "ObjCARCOpt::MoveCalls: Inserting new Retain: " << *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");
}
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");
}
}
if (!V) continue; // blotted
Instruction *Retain = cast<Instruction>(V);
+
+ DEBUG(dbgs() << "ObjCARCOpt::PerformCodePlacement: Visiting: " << *Retain
+ << "\n");
+
Value *Arg = GetObjCArg(Retain);
// If the object being released is in static or stack storage, we know it's
done:;
}
}
-
+
DEBUG(dbgs() << "ObjCARCOpt::OptimizeWeakCalls: Finished List.\n\n");
-
+
}
/// OptimizeSequences - Identify program paths which execute sequences of
// 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;
}
// 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);
}
DependingInstructions.clear();
Visited.clear();
}
-
+
DEBUG(dbgs() << "ObjCARCOpt::OptimizeReturns: Finished List.\n\n");
-
+
}
bool ObjCARCOpt::doInitialization(Module &M) {
Changed = true;
++NumPeeps;
+ DEBUG(dbgs() << "ObjCARCContract::ContractAutorelease: Fusing "
+ "retain/autorelease. Erasing: " << *Autorelease << "\n"
+ " Old Retain: "
+ << *Retain << "\n");
+
if (Class == IC_AutoreleaseRV)
Retain->setCalledFunction(getRetainAutoreleaseRVCallee(F.getParent()));
else
Retain->setCalledFunction(getRetainAutoreleaseCallee(F.getParent()));
+ DEBUG(dbgs() << " New Retain: "
+ << *Retain << "\n");
+
EraseInstruction(Autorelease);
return true;
}
SmallPtrSet<const BasicBlock *, 4> Visited;
for (inst_iterator I = inst_begin(&F), E = inst_end(&F); I != E; ) {
Instruction *Inst = &*I++;
-
+
DEBUG(dbgs() << "ObjCARCContract: Visiting: " << *Inst << "\n");
-
+
// Only these library routines return their argument. In particular,
// objc_retainBlock does not necessarily return its argument.
InstructionClass Class = GetBasicInstructionClass(Inst);
ConstantPointerNull::get(cast<PointerType>(CI->getType()));
Changed = true;
new StoreInst(Null, CI->getArgOperand(0), CI);
-
+
DEBUG(dbgs() << "OBJCARCContract: Old = " << *CI << "\n"
<< " New = " << *Null << "\n");
-
+
CI->replaceAllUsesWith(Null);
CI->eraseFromParent();
}