// This file also defines a simple ARC-aware AliasAnalysis.
//
// WARNING: This file knows about certain library functions. It recognizes them
-// by name, and hardwires knowedge of their semantics.
+// by name, and hardwires knowledge of their semantics.
//
// WARNING: This file knows about how certain Objective-C library functions are
// used. Naive LLVM IR transformations which would otherwise be
//===----------------------------------------------------------------------===//
#define DEBUG_TYPE "objc-arc"
-#include "llvm/Support/CommandLine.h"
#include "llvm/ADT/DenseMap.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/raw_ostream.h"
using namespace llvm;
// A handy option to enable/disable all optimizations in this file.
// ARC Utilities.
//===----------------------------------------------------------------------===//
-#include "llvm/Intrinsics.h"
-#include "llvm/Module.h"
+#include "llvm/ADT/StringSwitch.h"
#include "llvm/Analysis/ValueTracking.h"
-#include "llvm/Transforms/Utils/Local.h"
+#include "llvm/IR/Intrinsics.h"
+#include "llvm/IR/Module.h"
#include "llvm/Support/CallSite.h"
-#include "llvm/ADT/StringSwitch.h"
+#include "llvm/Transforms/Utils/Local.h"
namespace {
/// InstructionClass - A simple classification for instructions.
return isa<InvokeInst>(V) ? IC_CallOrUser : IC_User;
}
-/// IsRetain - Test if the the given class is objc_retain or
+/// IsRetain - Test if the given class is objc_retain or
/// equivalent.
static bool IsRetain(InstructionClass Class) {
return Class == IC_Retain ||
Class == IC_RetainRV;
}
-/// IsAutorelease - Test if the the given class is objc_autorelease or
+/// IsAutorelease - Test if the given class is objc_autorelease or
/// equivalent.
static bool IsAutorelease(InstructionClass Class) {
return Class == IC_Autorelease ||
// ARC AliasAnalysis.
//===----------------------------------------------------------------------===//
-#include "llvm/Pass.h"
#include "llvm/Analysis/AliasAnalysis.h"
#include "llvm/Analysis/Passes.h"
+#include "llvm/Pass.h"
namespace {
/// ObjCARCAliasAnalysis - This is a simple alias analysis
case IC_FusedRetainAutorelease:
case IC_FusedRetainAutoreleaseRV:
// These functions don't access any memory visible to the compiler.
- // Note that this doesn't include objc_retainBlock, becuase it updates
+ // Note that this doesn't include objc_retainBlock, because it updates
// pointers when it copies block data.
return NoModRef;
default:
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:
case IC_Autorelease:
case IC_AutoreleaseRV:
case IC_FusedRetainAutorelease:
- case IC_FusedRetainAutoreleaseRV:
+ case IC_FusedRetainAutoreleaseRV: {
// These calls return their argument verbatim, as a low-level
// optimization. However, this makes high-level optimizations
// harder. Undo any uses of this optimization that the front-end
// emitted here. We'll redo them in the contract pass.
Changed = true;
- Inst->replaceAllUsesWith(cast<CallInst>(Inst)->getArgOperand(0));
+ Value *Value = cast<CallInst>(Inst)->getArgOperand(0);
+ DEBUG(dbgs() << "ObjCARCExpand: Old = " << *Inst << "\n"
+ " New = " << *Value << "\n");
+ Inst->replaceAllUsesWith(Value);
break;
+ }
default:
break;
}
}
-
+
+ DEBUG(dbgs() << "ObjCARCExpand: Finished List.\n\n");
+
return Changed;
}
// ARC autorelease pool elimination.
//===----------------------------------------------------------------------===//
-#include "llvm/Constants.h"
#include "llvm/ADT/STLExtras.h"
+#include "llvm/IR/Constants.h"
namespace {
/// ObjCARCAPElim - Autorelease pool elimination.
// zap the pair.
if (Push && cast<CallInst>(Inst)->getArgOperand(0) == Push) {
Changed = true;
+ DEBUG(dbgs() << "ObjCARCAPElim::OptimizeBB: Zapping push pop autorelease pair:\n"
+ << " Pop: " << *Inst << "\n"
+ << " Push: " << *Push << "\n");
Inst->eraseFromParent();
Push->eraseFromParent();
}
// TODO: Delete release+retain pairs (rare).
-#include "llvm/LLVMContext.h"
-#include "llvm/Support/CFG.h"
-#include "llvm/ADT/Statistic.h"
#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/ADT/Statistic.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/Support/CFG.h"
STATISTIC(NumNoops, "Number of no-op objc calls eliminated");
STATISTIC(NumPartialNoops, "Number of partially no-op objc calls eliminated");
bool relatedSelect(const SelectInst *A, const Value *B);
bool relatedPHI(const PHINode *A, const Value *B);
- // Do not implement.
- void operator=(const ProvenanceAnalysis &);
- ProvenanceAnalysis(const ProvenanceAnalysis &);
+ void operator=(const ProvenanceAnalysis &) LLVM_DELETED_FUNCTION;
+ ProvenanceAnalysis(const ProvenanceAnalysis &) LLVM_DELETED_FUNCTION;
public:
ProvenanceAnalysis() {}
// An ObjC-Identified object can't alias a load if it is never locally stored.
if (AIsIdentified) {
+ // Check for an obvious escape.
+ if (isa<LoadInst>(B))
+ return isStoredObjCPointer(A);
if (BIsIdentified) {
- // If both pointers have provenance, they can be directly compared.
- if (A != B)
- return false;
- } else {
- if (isa<LoadInst>(B))
- return isStoredObjCPointer(A);
+ // Check for an obvious escape.
+ if (isa<LoadInst>(A))
+ return isStoredObjCPointer(B);
+ // Both pointers are identified and escapes aren't an evident problem.
+ return false;
}
- } else {
- if (BIsIdentified && isa<LoadInst>(A))
+ } else if (BIsIdentified) {
+ // Check for an obvious escape.
+ if (isa<LoadInst>(A))
return isStoredObjCPointer(B);
}
/// PtrState - This class summarizes several per-pointer runtime properties
/// which are propogated through the flow graph.
class PtrState {
- /// NestCount - The known minimum level of retain+release nesting.
- unsigned NestCount;
-
/// KnownPositiveRefCount - True if the reference count is known to
/// be incremented.
bool KnownPositiveRefCount;
/// TODO: Encapsulate this better.
RRInfo RRI;
- PtrState() : NestCount(0), KnownPositiveRefCount(false), Partial(false),
+ PtrState() : KnownPositiveRefCount(false), Partial(false),
Seq(S_None) {}
void SetKnownPositiveRefCount() {
return KnownPositiveRefCount;
}
- void IncrementNestCount() {
- if (NestCount != UINT_MAX) ++NestCount;
- }
-
- void DecrementNestCount() {
- if (NestCount != 0) --NestCount;
- }
-
- bool IsKnownNested() const {
- return NestCount > 0;
- }
-
void SetSeq(Sequence NewSeq) {
Seq = NewSeq;
}
PtrState::Merge(const PtrState &Other, bool TopDown) {
Seq = MergeSeqs(Seq, Other.Seq, TopDown);
KnownPositiveRefCount = KnownPositiveRefCount && Other.KnownPositiveRefCount;
- NestCount = std::min(NestCount, Other.NestCount);
// We can't merge a plain objc_retain with an objc_retainBlock.
if (RRI.IsRetainBlock != Other.RRI.IsRetainBlock)
// loop backedge. Loop backedges are special.
TopDownPathCount += Other.TopDownPathCount;
+ // Check for overflow. If we have overflow, fall back to conservative behavior.
+ if (TopDownPathCount < Other.TopDownPathCount) {
+ clearTopDownPointers();
+ return;
+ }
+
// For each entry in the other set, if our set has an entry with the same key,
// merge the entries. Otherwise, copy the entry and merge it with an empty
// entry.
// loop backedge. Loop backedges are special.
BottomUpPathCount += Other.BottomUpPathCount;
+ // Check for overflow. If we have overflow, fall back to conservative behavior.
+ if (BottomUpPathCount < Other.BottomUpPathCount) {
+ clearBottomUpPointers();
+ return;
+ }
+
// For each entry in the other set, if our set has an entry with the
// same key, merge the entries. Otherwise, copy the entry and merge
// it with an empty entry.
Type *I8X = PointerType::getUnqual(Type::getInt8Ty(C));
Type *Params[] = { I8X };
FunctionType *FTy = FunctionType::get(I8X, Params, /*isVarArg=*/false);
- AttrListPtr Attributes = AttrListPtr().addAttr(~0u, Attribute::NoUnwind);
+ AttributeSet Attribute =
+ AttributeSet().addAttr(M->getContext(), AttributeSet::FunctionIndex,
+ Attribute::get(C, Attribute::NoUnwind));
RetainRVCallee =
M->getOrInsertFunction("objc_retainAutoreleasedReturnValue", FTy,
- Attributes);
+ Attribute);
}
return RetainRVCallee;
}
Type *I8X = PointerType::getUnqual(Type::getInt8Ty(C));
Type *Params[] = { I8X };
FunctionType *FTy = FunctionType::get(I8X, Params, /*isVarArg=*/false);
- AttrListPtr Attributes = AttrListPtr().addAttr(~0u, Attribute::NoUnwind);
+ AttributeSet Attribute =
+ AttributeSet().addAttr(M->getContext(), AttributeSet::FunctionIndex,
+ Attribute::get(C, Attribute::NoUnwind));
AutoreleaseRVCallee =
M->getOrInsertFunction("objc_autoreleaseReturnValue", FTy,
- Attributes);
+ Attribute);
}
return AutoreleaseRVCallee;
}
if (!ReleaseCallee) {
LLVMContext &C = M->getContext();
Type *Params[] = { PointerType::getUnqual(Type::getInt8Ty(C)) };
- AttrListPtr Attributes = AttrListPtr().addAttr(~0u, Attribute::NoUnwind);
+ AttributeSet Attribute =
+ AttributeSet().addAttr(M->getContext(), AttributeSet::FunctionIndex,
+ Attribute::get(C, Attribute::NoUnwind));
ReleaseCallee =
M->getOrInsertFunction(
"objc_release",
FunctionType::get(Type::getVoidTy(C), Params, /*isVarArg=*/false),
- Attributes);
+ Attribute);
}
return ReleaseCallee;
}
if (!RetainCallee) {
LLVMContext &C = M->getContext();
Type *Params[] = { PointerType::getUnqual(Type::getInt8Ty(C)) };
- AttrListPtr Attributes = AttrListPtr().addAttr(~0u, Attribute::NoUnwind);
+ AttributeSet Attribute =
+ AttributeSet().addAttr(M->getContext(), AttributeSet::FunctionIndex,
+ Attribute::get(C, Attribute::NoUnwind));
RetainCallee =
M->getOrInsertFunction(
"objc_retain",
FunctionType::get(Params[0], Params, /*isVarArg=*/false),
- Attributes);
+ Attribute);
}
return RetainCallee;
}
M->getOrInsertFunction(
"objc_retainBlock",
FunctionType::get(Params[0], Params, /*isVarArg=*/false),
- AttrListPtr());
+ AttributeSet());
}
return RetainBlockCallee;
}
if (!AutoreleaseCallee) {
LLVMContext &C = M->getContext();
Type *Params[] = { PointerType::getUnqual(Type::getInt8Ty(C)) };
- AttrListPtr Attributes = AttrListPtr().addAttr(~0u, Attribute::NoUnwind);
+ AttributeSet Attribute =
+ AttributeSet().addAttr(M->getContext(), AttributeSet::FunctionIndex,
+ Attribute::get(C, Attribute::NoUnwind));
AutoreleaseCallee =
M->getOrInsertFunction(
"objc_autorelease",
FunctionType::get(Params[0], Params, /*isVarArg=*/false),
- Attributes);
+ Attribute);
}
return AutoreleaseCallee;
}
+/// IsPotentialUse - Test whether the given value is possible a
+/// reference-counted pointer, including tests which utilize AliasAnalysis.
+static bool IsPotentialUse(const Value *Op, AliasAnalysis &AA) {
+ // First make the rudimentary check.
+ if (!IsPotentialUse(Op))
+ return false;
+
+ // Objects in constant memory are not reference-counted.
+ if (AA.pointsToConstantMemory(Op))
+ return false;
+
+ // Pointers in constant memory are not pointing to reference-counted objects.
+ if (const LoadInst *LI = dyn_cast<LoadInst>(Op))
+ if (AA.pointsToConstantMemory(LI->getPointerOperand()))
+ return false;
+
+ // Otherwise assume the worst.
+ return true;
+}
+
/// CanAlterRefCount - Test whether the given instruction can result in a
/// reference count modification (positive or negative) for the pointer's
/// object.
for (ImmutableCallSite::arg_iterator I = CS.arg_begin(), E = CS.arg_end();
I != E; ++I) {
const Value *Op = *I;
- if (IsPotentialUse(Op) && PA.related(Ptr, Op))
+ if (IsPotentialUse(Op, *PA.getAA()) && PA.related(Ptr, Op))
return true;
}
return false;
// Comparing a pointer with null, or any other constant, isn't really a use,
// because we don't care what the pointer points to, or about the values
// of any other dynamic reference-counted pointers.
- if (!IsPotentialUse(ICI->getOperand(1)))
+ if (!IsPotentialUse(ICI->getOperand(1), *PA.getAA()))
return false;
} else if (ImmutableCallSite CS = static_cast<const Value *>(Inst)) {
// For calls, just check the arguments (and not the callee operand).
for (ImmutableCallSite::arg_iterator OI = CS.arg_begin(),
OE = CS.arg_end(); OI != OE; ++OI) {
const Value *Op = *OI;
- if (IsPotentialUse(Op) && PA.related(Ptr, Op))
+ if (IsPotentialUse(Op, *PA.getAA()) && PA.related(Ptr, Op))
return true;
}
return false;
const Value *Op = GetUnderlyingObjCPtr(SI->getPointerOperand());
// If we can't tell what the underlying object was, assume there is a
// dependence.
- return IsPotentialUse(Op) && PA.related(Op, Ptr);
+ return IsPotentialUse(Op, *PA.getAA()) && PA.related(Op, Ptr);
}
// Check each operand for a match.
for (User::const_op_iterator OI = Inst->op_begin(), OE = Inst->op_end();
OI != OE; ++OI) {
const Value *Op = *OI;
- if (IsPotentialUse(Op) && PA.related(Ptr, Op))
+ if (IsPotentialUse(Op, *PA.getAA()) && PA.related(Ptr, Op))
return true;
}
return false;
// 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: "
+ << *Retain << "\n");
}
/// OptimizeRetainRVCall - Turn objc_retainAutoreleasedReturnValue into
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: "
+ << *RetainRV << "\n");
+
return false;
}
Changed = true;
++NumPeeps;
+
+ DEBUG(dbgs() << "ObjCARCOpt::OptimizeAutoreleaseRVCall: Transforming "
+ "objc_autoreleaseReturnValue => "
+ "objc_autorelease since its operand is not used as a return "
+ "value.\n"
+ " Old: "
+ << *AutoreleaseRV << "\n");
+
cast<CallInst>(AutoreleaseRV)->
setCalledFunction(getAutoreleaseCallee(F.getParent()));
+
+ DEBUG(dbgs() << " New: "
+ << *AutoreleaseRV << "\n");
+
}
/// OptimizeIndividualCalls - Visit each call, one at a time, and make
// Visit all objc_* calls in F.
for (inst_iterator I = inst_begin(&F), E = inst_end(&F); I != E; ) {
Instruction *Inst = &*I++;
+
+ DEBUG(dbgs() << "ObjCARCOpt::OptimizeIndividualCalls: Visiting: " <<
+ *Inst << "\n");
+
InstructionClass Class = GetBasicInstructionClass(Inst);
switch (Class) {
case IC_NoopCast:
Changed = true;
++NumNoops;
+ DEBUG(dbgs() << "ObjCARCOpt::OptimizeIndividualCalls: Erasing no-op cast:"
+ " " << *Inst << "\n");
EraseInstruction(Inst);
continue;
}
}
} while (!Worklist.empty());
+
+ DEBUG(dbgs() << "ObjCARCOpt::OptimizeIndividualCalls: Finished Queue.\n\n");
+
}
}
MDNode *ReleaseMetadata = Inst->getMetadata(ImpreciseReleaseMDKind);
S.ResetSequenceProgress(ReleaseMetadata ? S_MovableRelease : S_Release);
S.RRI.ReleaseMetadata = ReleaseMetadata;
- S.RRI.KnownSafe = S.IsKnownNested() || S.IsKnownIncremented();
+ S.RRI.KnownSafe = S.IsKnownIncremented();
S.RRI.IsTailCallRelease = cast<CallInst>(Inst)->isTailCall();
S.RRI.Calls.insert(Inst);
- S.IncrementNestCount();
+ S.SetKnownPositiveRefCount();
break;
}
case IC_RetainBlock:
PtrState &S = MyStates.getPtrBottomUpState(Arg);
S.SetKnownPositiveRefCount();
- S.DecrementNestCount();
switch (S.GetSeq()) {
case S_Stop:
// Merge the states from each successor to compute the initial state
// for the current block.
- for (BBState::edge_iterator SI(MyStates.succ_begin()),
- SE(MyStates.succ_end()); SI != SE; ++SI) {
+ BBState::edge_iterator SI(MyStates.succ_begin()),
+ SE(MyStates.succ_end());
+ if (SI != SE) {
const BasicBlock *Succ = *SI;
DenseMap<const BasicBlock *, BBState>::iterator I = BBStates.find(Succ);
assert(I != BBStates.end());
assert(I != BBStates.end());
MyStates.MergeSucc(I->second);
}
- break;
}
// Visit all the instructions, bottom-up.
S.ResetSequenceProgress(S_Retain);
S.RRI.IsRetainBlock = Class == IC_RetainBlock;
- // Don't check S.IsKnownIncremented() here because it's not sufficient.
- S.RRI.KnownSafe = S.IsKnownNested();
+ S.RRI.KnownSafe = S.IsKnownIncremented();
S.RRI.Calls.insert(Inst);
}
- S.IncrementNestCount();
- return NestingDetected;
+ S.SetKnownPositiveRefCount();
+
+ // A retain can be a potential use; procede to the generic checking
+ // code below.
+ break;
}
case IC_Release: {
Arg = GetObjCArg(Inst);
PtrState &S = MyStates.getPtrTopDownState(Arg);
- S.DecrementNestCount();
+ S.ClearRefCount();
switch (S.GetSeq()) {
case S_Retain:
// Merge the states from each predecessor to compute the initial state
// for the current block.
- for (BBState::edge_iterator PI(MyStates.pred_begin()),
- PE(MyStates.pred_end()); PI != PE; ++PI) {
+ BBState::edge_iterator PI(MyStates.pred_begin()),
+ PE(MyStates.pred_end());
+ if (PI != PE) {
const BasicBlock *Pred = *PI;
DenseMap<const BasicBlock *, BBState>::iterator I = BBStates.find(Pred);
assert(I != BBStates.end());
assert(I != BBStates.end());
MyStates.MergePred(I->second);
}
- break;
}
// Visit all the instructions, top-down.
// 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");
+
InstructionClass Class = GetBasicInstructionClass(Inst);
if (Class != IC_LoadWeak && Class != IC_LoadWeakRetained)
continue;
// No return value.
break;
default:
- break;
+ llvm_unreachable("alloca really is used!");
}
UserInst->eraseFromParent();
}
done:;
}
}
+
+ DEBUG(dbgs() << "ObjCARCOpt::OptimizeWeakCalls: Finished List.\n\n");
+
}
/// OptimizeSequences - Identify program paths which execute sequences of
}
/// OptimizeReturns - Look for this pattern:
-///
+/// \code
/// %call = call i8* @something(...)
/// %2 = call i8* @objc_retain(i8* %call)
/// %3 = call i8* @objc_autorelease(i8* %2)
/// 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())
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");
+
if (!Ret) continue;
const Value *Arg = StripPointerCastsAndObjCCalls(Ret->getOperand(0));
DependingInstructions.clear();
Visited.clear();
}
+
+ DEBUG(dbgs() << "ObjCARCOpt::OptimizeReturns: Finished List.\n\n");
+
}
bool ObjCARCOpt::doInitialization(Module &M) {
// TODO: ObjCARCContract could insert PHI nodes when uses aren't
// dominated by single calls.
-#include "llvm/Operator.h"
-#include "llvm/InlineAsm.h"
#include "llvm/Analysis/Dominators.h"
+#include "llvm/IR/InlineAsm.h"
+#include "llvm/IR/Operator.h"
STATISTIC(NumStoreStrongs, "Number objc_storeStrong calls formed");
Type *I8XX = PointerType::getUnqual(I8X);
Type *Params[] = { I8XX, I8X };
- AttrListPtr Attributes = AttrListPtr()
- .addAttr(~0u, Attribute::NoUnwind)
- .addAttr(1, Attribute::NoCapture);
+ AttributeSet Attribute = AttributeSet()
+ .addAttr(M->getContext(), AttributeSet::FunctionIndex,
+ Attribute::get(C, Attribute::NoUnwind))
+ .addAttr(M->getContext(), 1, Attribute::get(C, Attribute::NoCapture));
StoreStrongCallee =
M->getOrInsertFunction(
"objc_storeStrong",
FunctionType::get(Type::getVoidTy(C), Params, /*isVarArg=*/false),
- Attributes);
+ Attribute);
}
return StoreStrongCallee;
}
Type *I8X = PointerType::getUnqual(Type::getInt8Ty(C));
Type *Params[] = { I8X };
FunctionType *FTy = FunctionType::get(I8X, Params, /*isVarArg=*/false);
- AttrListPtr Attributes = AttrListPtr().addAttr(~0u, Attribute::NoUnwind);
+ AttributeSet Attribute =
+ AttributeSet().addAttr(M->getContext(), AttributeSet::FunctionIndex,
+ Attribute::get(C, Attribute::NoUnwind));
RetainAutoreleaseCallee =
- M->getOrInsertFunction("objc_retainAutorelease", FTy, Attributes);
+ M->getOrInsertFunction("objc_retainAutorelease", FTy, Attribute);
}
return RetainAutoreleaseCallee;
}
Type *I8X = PointerType::getUnqual(Type::getInt8Ty(C));
Type *Params[] = { I8X };
FunctionType *FTy = FunctionType::get(I8X, Params, /*isVarArg=*/false);
- AttrListPtr Attributes = AttrListPtr().addAttr(~0u, Attribute::NoUnwind);
+ AttributeSet Attribute =
+ AttributeSet().addAttr(M->getContext(), AttributeSet::FunctionIndex,
+ Attribute::get(C, Attribute::NoUnwind));
RetainAutoreleaseRVCallee =
M->getOrInsertFunction("objc_retainAutoreleaseReturnValue", FTy,
- Attributes);
+ Attribute);
}
return RetainAutoreleaseRVCallee;
}
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);
if (!RetainRVMarker)
break;
BasicBlock::iterator BBI = Inst;
- --BBI;
- while (isNoopInstruction(BBI)) --BBI;
+ BasicBlock *InstParent = Inst->getParent();
+
+ // Step up to see if the call immediately precedes the RetainRV call.
+ // If it's an invoke, we have to cross a block boundary. And we have
+ // to carefully dodge no-op instructions.
+ do {
+ if (&*BBI == InstParent->begin()) {
+ BasicBlock *Pred = InstParent->getSinglePredecessor();
+ if (!Pred)
+ goto decline_rv_optimization;
+ BBI = Pred->getTerminator();
+ break;
+ }
+ --BBI;
+ } while (isNoopInstruction(BBI));
+
if (&*BBI == GetObjCArg(Inst)) {
+ DEBUG(dbgs() << "ObjCARCContract: Adding inline asm marker for "
+ "retainAutoreleasedReturnValue optimization.\n");
Changed = true;
InlineAsm *IA =
InlineAsm::get(FunctionType::get(Type::getVoidTy(Inst->getContext()),
/*Constraints=*/"", /*hasSideEffects=*/true);
CallInst::Create(IA, "", Inst);
}
+ decline_rv_optimization:
break;
}
case IC_InitWeak: {
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();
}
continue;
}
+ DEBUG(dbgs() << "ObjCARCContract: Finished List.\n\n");
+
// Don't use GetObjCArg because we don't want to look through bitcasts
// and such; to do the replacement, the argument must have type i8*.
const Value *Arg = cast<CallInst>(Inst)->getArgOperand(0);