#define DEBUG_TYPE "objc-arc-opts"
#include "ObjCARC.h"
+#include "ARCRuntimeEntryPoints.h"
#include "DependencyAnalysis.h"
#include "ObjCARCAliasAnalysis.h"
#include "ProvenanceAnalysis.h"
/// occured, false otherwise.
bool Merge(const RRInfo &Other);
- bool IsTrackingImpreciseReleases() {
- return ReleaseMetadata != 0;
- }
};
}
/// The current position in the sequence.
Sequence Seq : 8;
- public:
/// Unidirectional information about the current sequence.
- ///
- /// TODO: Encapsulate this better.
RRInfo RRI;
+ public:
PtrState() : KnownPositiveRefCount(false), Partial(false),
Seq(S_None) {}
RRI.KnownSafe = NewValue;
}
+ bool IsTailCallRelease() const {
+ return RRI.IsTailCallRelease;
+ }
+
+ void SetTailCallRelease(const bool NewValue) {
+ RRI.IsTailCallRelease = NewValue;
+ }
+
+ bool IsTrackingImpreciseReleases() const {
+ return RRI.ReleaseMetadata != 0;
+ }
+
+ const MDNode *GetReleaseMetadata() const {
+ return RRI.ReleaseMetadata;
+ }
+
+ void SetReleaseMetadata(MDNode *NewValue) {
+ RRI.ReleaseMetadata = NewValue;
+ }
+
+ bool IsCFGHazardAfflicted() const {
+ return RRI.CFGHazardAfflicted;
+ }
+
+ void SetCFGHazardAfflicted(const bool NewValue) {
+ RRI.CFGHazardAfflicted = NewValue;
+ }
+
void SetKnownPositiveRefCount() {
DEBUG(dbgs() << "Setting Known Positive.\n");
KnownPositiveRefCount = true;
}
void Merge(const PtrState &Other, bool TopDown);
+
+ void InsertCall(Instruction *I) {
+ RRI.Calls.insert(I);
+ }
+
+ void InsertReverseInsertPt(Instruction *I) {
+ RRI.ReverseInsertPts.insert(I);
+ }
+
+ void ClearReverseInsertPts() {
+ RRI.ReverseInsertPts.clear();
+ }
+
+ bool HasReverseInsertPts() const {
+ return !RRI.ReverseInsertPts.empty();
+ }
+
+ const RRInfo &GetRRInfo() const {
+ return RRI;
+ }
};
}
class ObjCARCOpt : public FunctionPass {
bool Changed;
ProvenanceAnalysis PA;
+ ARCRuntimeEntryPoints EP;
// This is used to track if a pointer is stored into an alloca.
DenseSet<const Value *> MultiOwnersSet;
/// A flag indicating whether this optimization pass should run.
bool Run;
- /// Declarations for ObjC runtime functions, for use in creating calls to
- /// them. These are initialized lazily to avoid cluttering up the Module
- /// with unused declarations.
-
- /// Declaration for ObjC runtime function objc_autoreleaseReturnValue.
- Constant *AutoreleaseRVCallee;
- /// Declaration for ObjC runtime function objc_release.
- Constant *ReleaseCallee;
- /// Declaration for ObjC runtime function objc_retain.
- Constant *RetainCallee;
- /// Declaration for ObjC runtime function objc_retainBlock.
- Constant *RetainBlockCallee;
- /// Declaration for ObjC runtime function objc_autorelease.
- Constant *AutoreleaseCallee;
-
/// Flags which determine whether each of the interesting runtine functions
/// is in fact used in the current function.
unsigned UsedInThisFunction;
unsigned ARCAnnotationProvenanceSourceMDKind;
#endif // ARC_ANNOATIONS
- Constant *getAutoreleaseRVCallee(Module *M);
- Constant *getReleaseCallee(Module *M);
- Constant *getRetainCallee(Module *M);
- Constant *getRetainBlockCallee(Module *M);
- Constant *getAutoreleaseCallee(Module *M);
-
bool IsRetainBlockOptimizable(const Instruction *Inst);
bool OptimizeRetainRVCall(Function &F, Instruction *RetainRV);
return true;
}
-Constant *ObjCARCOpt::getAutoreleaseRVCallee(Module *M) {
- if (!AutoreleaseRVCallee) {
- LLVMContext &C = M->getContext();
- Type *I8X = PointerType::getUnqual(Type::getInt8Ty(C));
- Type *Params[] = { I8X };
- FunctionType *FTy = FunctionType::get(I8X, Params, /*isVarArg=*/false);
- AttributeSet Attribute =
- AttributeSet().addAttribute(M->getContext(), AttributeSet::FunctionIndex,
- Attribute::NoUnwind);
- AutoreleaseRVCallee =
- M->getOrInsertFunction("objc_autoreleaseReturnValue", FTy,
- Attribute);
- }
- return AutoreleaseRVCallee;
-}
-
-Constant *ObjCARCOpt::getReleaseCallee(Module *M) {
- if (!ReleaseCallee) {
- LLVMContext &C = M->getContext();
- Type *Params[] = { PointerType::getUnqual(Type::getInt8Ty(C)) };
- AttributeSet Attribute =
- AttributeSet().addAttribute(M->getContext(), AttributeSet::FunctionIndex,
- Attribute::NoUnwind);
- ReleaseCallee =
- M->getOrInsertFunction(
- "objc_release",
- FunctionType::get(Type::getVoidTy(C), Params, /*isVarArg=*/false),
- Attribute);
- }
- return ReleaseCallee;
-}
-
-Constant *ObjCARCOpt::getRetainCallee(Module *M) {
- if (!RetainCallee) {
- LLVMContext &C = M->getContext();
- Type *Params[] = { PointerType::getUnqual(Type::getInt8Ty(C)) };
- AttributeSet Attribute =
- AttributeSet().addAttribute(M->getContext(), AttributeSet::FunctionIndex,
- Attribute::NoUnwind);
- RetainCallee =
- M->getOrInsertFunction(
- "objc_retain",
- FunctionType::get(Params[0], Params, /*isVarArg=*/false),
- Attribute);
- }
- return RetainCallee;
-}
-
-Constant *ObjCARCOpt::getRetainBlockCallee(Module *M) {
- if (!RetainBlockCallee) {
- LLVMContext &C = M->getContext();
- Type *Params[] = { PointerType::getUnqual(Type::getInt8Ty(C)) };
- // objc_retainBlock is not nounwind because it calls user copy constructors
- // which could theoretically throw.
- RetainBlockCallee =
- M->getOrInsertFunction(
- "objc_retainBlock",
- FunctionType::get(Params[0], Params, /*isVarArg=*/false),
- AttributeSet());
- }
- return RetainBlockCallee;
-}
-
-Constant *ObjCARCOpt::getAutoreleaseCallee(Module *M) {
- if (!AutoreleaseCallee) {
- LLVMContext &C = M->getContext();
- Type *Params[] = { PointerType::getUnqual(Type::getInt8Ty(C)) };
- AttributeSet Attribute =
- AttributeSet().addAttribute(M->getContext(), AttributeSet::FunctionIndex,
- Attribute::NoUnwind);
- AutoreleaseCallee =
- M->getOrInsertFunction(
- "objc_autorelease",
- FunctionType::get(Params[0], Params, /*isVarArg=*/false),
- Attribute);
- }
- return AutoreleaseCallee;
-}
-
/// Turn objc_retainAutoreleasedReturnValue into objc_retain if the operand is
/// not a return value. Or, if it can be paired with an
/// objc_autoreleaseReturnValue, delete the pair and return true.
"objc_retain since the operand is not a return value.\n"
"Old = " << *RetainRV << "\n");
- cast<CallInst>(RetainRV)->setCalledFunction(getRetainCallee(F.getParent()));
+ Constant *NewDecl = EP.get(ARCRuntimeEntryPoints::EPT_Retain);
+ cast<CallInst>(RetainRV)->setCalledFunction(NewDecl);
DEBUG(dbgs() << "New = " << *RetainRV << "\n");
"Old = " << *AutoreleaseRV << "\n");
CallInst *AutoreleaseRVCI = cast<CallInst>(AutoreleaseRV);
- AutoreleaseRVCI->
- setCalledFunction(getAutoreleaseCallee(F.getParent()));
+ Constant *NewDecl = EP.get(ARCRuntimeEntryPoints::EPT_Autorelease);
+ AutoreleaseRVCI->setCalledFunction(NewDecl);
AutoreleaseRVCI->setTailCall(false); // Never tail call objc_autorelease.
Class = IC_Autorelease;
DEBUG(dbgs() << "Strength reduced retainBlock => retain.\n");
DEBUG(dbgs() << "Old: " << *Inst << "\n");
CallInst *RetainBlock = cast<CallInst>(Inst);
- RetainBlock->setCalledFunction(getRetainCallee(F.getParent()));
+ Constant *NewDecl = EP.get(ARCRuntimeEntryPoints::EPT_Retain);
+ RetainBlock->setCalledFunction(NewDecl);
// Remove copy_on_escape metadata.
RetainBlock->setMetadata(CopyOnEscapeMDKind, 0);
Class = IC_Retain;
// Create the declaration lazily.
LLVMContext &C = Inst->getContext();
- CallInst *NewCall =
- CallInst::Create(getReleaseCallee(F.getParent()),
- Call->getArgOperand(0), "", Call);
+
+ Constant *Decl = EP.get(ARCRuntimeEntryPoints::EPT_Release);
+ CallInst *NewCall = CallInst::Create(Decl, Call->getArgOperand(0), "",
+ Call);
NewCall->setMetadata(ImpreciseReleaseMDKind, MDNode::get(C, None));
DEBUG(dbgs() << "Replacing autorelease{,RV}(x) with objc_release(x) "
S.ClearSequenceProgress();
break;
}
- S.RRI.CFGHazardAfflicted = true;
+ S.SetCFGHazardAfflicted(true);
ShouldContinue = true;
break;
}
// safe, stop code motion. This is because whether or not it is safe to
// remove RR pairs via KnownSafe is an orthogonal concept to whether we
// are allowed to perform code motion.
- S.RRI.CFGHazardAfflicted = true;
+ S.SetCFGHazardAfflicted(true);
}
}
}
Sequence NewSeq = ReleaseMetadata ? S_MovableRelease : S_Release;
ANNOTATE_BOTTOMUP(Inst, Arg, S.GetSeq(), NewSeq);
S.ResetSequenceProgress(NewSeq);
- S.RRI.ReleaseMetadata = ReleaseMetadata;
+ S.SetReleaseMetadata(ReleaseMetadata);
S.SetKnownSafe(S.HasKnownPositiveRefCount());
- S.RRI.IsTailCallRelease = cast<CallInst>(Inst)->isTailCall();
- S.RRI.Calls.insert(Inst);
+ S.SetTailCallRelease(cast<CallInst>(Inst)->isTailCall());
+ S.InsertCall(Inst);
S.SetKnownPositiveRefCount();
break;
}
case S_Use:
// 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();
+ if (OldSeq != S_Use || S.IsTrackingImpreciseReleases())
+ S.ClearReverseInsertPts();
// FALL THROUGH
case S_CanRelease:
// Don't do retain+release tracking for IC_RetainRV, because it's
// better to let it remain as the first instruction after a call.
if (Class != IC_RetainRV)
- Retains[Inst] = S.RRI;
+ Retains[Inst] = S.GetRRInfo();
S.ClearSequenceProgress();
break;
case S_None:
if (CanUse(Inst, Ptr, PA, Class)) {
DEBUG(dbgs() << "CanUse: Seq: " << Seq << "; " << *Ptr
<< "\n");
- assert(S.RRI.ReverseInsertPts.empty());
+ assert(!S.HasReverseInsertPts());
// 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
// in its own block, and we don't want to split critical edges.
if (isa<InvokeInst>(Inst))
- S.RRI.ReverseInsertPts.insert(BB->getFirstInsertionPt());
+ S.InsertReverseInsertPt(BB->getFirstInsertionPt());
else
- S.RRI.ReverseInsertPts.insert(llvm::next(BasicBlock::iterator(Inst)));
+ S.InsertReverseInsertPt(llvm::next(BasicBlock::iterator(Inst)));
S.SetSeq(S_Use);
ANNOTATE_BOTTOMUP(Inst, Ptr, Seq, S_Use);
} else if (Seq == S_Release && IsUser(Class)) {
// Non-movable releases depend on any possible objc pointer use.
S.SetSeq(S_Stop);
ANNOTATE_BOTTOMUP(Inst, Ptr, S_Release, S_Stop);
- assert(S.RRI.ReverseInsertPts.empty());
+ assert(!S.HasReverseInsertPts());
// As above; handle invoke specially.
if (isa<InvokeInst>(Inst))
- S.RRI.ReverseInsertPts.insert(BB->getFirstInsertionPt());
+ S.InsertReverseInsertPt(BB->getFirstInsertionPt());
else
- S.RRI.ReverseInsertPts.insert(llvm::next(BasicBlock::iterator(Inst)));
+ S.InsertReverseInsertPt(llvm::next(BasicBlock::iterator(Inst)));
}
break;
case S_Stop:
ANNOTATE_TOPDOWN(Inst, Arg, S.GetSeq(), S_Retain);
S.ResetSequenceProgress(S_Retain);
S.SetKnownSafe(S.HasKnownPositiveRefCount());
- S.RRI.Calls.insert(Inst);
+ S.InsertCall(Inst);
}
S.SetKnownPositiveRefCount();
case S_Retain:
case S_CanRelease:
if (OldSeq == S_Retain || ReleaseMetadata != 0)
- S.RRI.ReverseInsertPts.clear();
+ S.ClearReverseInsertPts();
// FALL THROUGH
case S_Use:
- S.RRI.ReleaseMetadata = ReleaseMetadata;
- S.RRI.IsTailCallRelease = cast<CallInst>(Inst)->isTailCall();
- Releases[Inst] = S.RRI;
+ S.SetReleaseMetadata(ReleaseMetadata);
+ S.SetTailCallRelease(cast<CallInst>(Inst)->isTailCall());
+ Releases[Inst] = S.GetRRInfo();
ANNOTATE_TOPDOWN(Inst, Arg, S.GetSeq(), S_None);
S.ClearSequenceProgress();
break;
case S_Retain:
S.SetSeq(S_CanRelease);
ANNOTATE_TOPDOWN(Inst, Ptr, Seq, S_CanRelease);
- assert(S.RRI.ReverseInsertPts.empty());
- S.RRI.ReverseInsertPts.insert(Inst);
+ assert(!S.HasReverseInsertPts());
+ S.InsertReverseInsertPt(Inst);
// One call can't cause a transition from S_Retain to S_CanRelease
// and S_CanRelease to S_Use. If we've made the first transition,
Instruction *InsertPt = *PI;
Value *MyArg = ArgTy == ParamTy ? Arg :
new BitCastInst(Arg, ParamTy, "", InsertPt);
- CallInst *Call =
- CallInst::Create(getRetainCallee(M), MyArg, "", InsertPt);
+ Constant *Decl = EP.get(ARCRuntimeEntryPoints::EPT_Retain);
+ CallInst *Call = CallInst::Create(Decl, MyArg, "", InsertPt);
Call->setDoesNotThrow();
Call->setTailCall();
Instruction *InsertPt = *PI;
Value *MyArg = ArgTy == ParamTy ? Arg :
new BitCastInst(Arg, ParamTy, "", InsertPt);
- CallInst *Call = CallInst::Create(getReleaseCallee(M), MyArg,
- "", InsertPt);
+ Constant *Decl = EP.get(ARCRuntimeEntryPoints::EPT_Release);
+ CallInst *Call = CallInst::Create(Decl, MyArg, "", InsertPt);
// Attach a clang.imprecise_release metadata tag, if appropriate.
if (MDNode *M = ReleasesToMove.ReleaseMetadata)
Call->setMetadata(ImpreciseReleaseMDKind, M);
Changed = true;
// If the load has a builtin retain, insert a plain retain for it.
if (Class == IC_LoadWeakRetained) {
- CallInst *CI =
- CallInst::Create(getRetainCallee(F.getParent()), EarlierCall,
- "", Call);
+ Constant *Decl = EP.get(ARCRuntimeEntryPoints::EPT_Retain);
+ CallInst *CI = CallInst::Create(Decl, EarlierCall, "", Call);
CI->setTailCall();
}
// Zap the fully redundant load.
Changed = true;
// If the load has a builtin retain, insert a plain retain for it.
if (Class == IC_LoadWeakRetained) {
- CallInst *CI =
- CallInst::Create(getRetainCallee(F.getParent()), EarlierCall,
- "", Call);
+ Constant *Decl = EP.get(ARCRuntimeEntryPoints::EPT_Retain);
+ CallInst *CI = CallInst::Create(Decl, EarlierCall, "", Call);
CI->setTailCall();
}
// Zap the fully redundant load.
// they are not, because they return their argument value. And objc_release
// calls finalizers which can have arbitrary side effects.
- // These are initialized lazily.
- AutoreleaseRVCallee = 0;
- ReleaseCallee = 0;
- RetainCallee = 0;
- RetainBlockCallee = 0;
- AutoreleaseCallee = 0;
+ // Initialize our runtime entry point cache.
+ EP.Initialize(&M);
return false;
}