// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
-//
-// This file defines ObjC ARC optimizations. ARC stands for
-// Automatic Reference Counting and is a system for managing reference counts
-// for objects in Objective C.
-//
-// The optimizations performed include elimination of redundant, partially
-// redundant, and inconsequential reference count operations, elimination of
-// redundant weak pointer operations, pattern-matching and replacement of
-// low-level operations into higher-level operations, and numerous minor
-// simplifications.
-//
-// This file also defines a simple ARC-aware AliasAnalysis.
-//
-// WARNING: This file knows about certain library functions. It recognizes them
-// 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
-// behavior-preserving may break these assumptions.
-//
+/// \file
+/// This file defines ObjC ARC optimizations. ARC stands for Automatic
+/// Reference Counting and is a system for managing reference counts for objects
+/// in Objective C.
+///
+/// The optimizations performed include elimination of redundant, partially
+/// redundant, and inconsequential reference count operations, elimination of
+/// redundant weak pointer operations, pattern-matching and replacement of
+/// low-level operations into higher-level operations, and numerous minor
+/// simplifications.
+///
+/// This file also defines a simple ARC-aware AliasAnalysis.
+///
+/// WARNING: This file knows about certain library functions. It recognizes them
+/// 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
+/// behavior-preserving may break these assumptions.
+///
//===----------------------------------------------------------------------===//
#define DEBUG_TYPE "objc-arc"
-#include "llvm/Support/raw_ostream.h"
-#include "llvm/Support/CommandLine.h"
#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/SmallPtrSet.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.
+/// \brief A handy option to enable/disable all optimizations in this file.
static cl::opt<bool> EnableARCOpts("enable-objc-arc-opts", cl::init(true));
-//===----------------------------------------------------------------------===//
-// Misc. Utilities
-//===----------------------------------------------------------------------===//
+/// \defgroup MiscUtils Miscellaneous utilities that are not ARC specific.
+/// @{
namespace {
- /// MapVector - An associative container with fast insertion-order
- /// (deterministic) iteration over its elements. Plus the special
- /// blot operation.
+ /// \brief An associative container with fast insertion-order (deterministic)
+ /// iteration over its elements. Plus the special blot operation.
template<class KeyT, class ValueT>
class MapVector {
- /// Map - Map keys to indices in Vector.
+ /// Map keys to indices in Vector.
typedef DenseMap<KeyT, size_t> MapTy;
MapTy Map;
- /// Vector - Keys and values.
typedef std::vector<std::pair<KeyT, ValueT> > VectorTy;
+ /// Keys and values.
VectorTy Vector;
public:
return Vector.begin() + It->second;
}
- /// blot - This is similar to erase, but instead of removing the element
- /// from the vector, it just zeros out the key in the vector. This leaves
- /// iterators intact, but clients must be prepared for zeroed-out keys when
- /// iterating.
+ /// This is similar to erase, but instead of removing the element from the
+ /// vector, it just zeros out the key in the vector. This leaves iterators
+ /// intact, but clients must be prepared for zeroed-out keys when iterating.
void blot(const KeyT &Key) {
typename MapTy::iterator It = Map.find(Key);
if (It == Map.end()) return;
};
}
-//===----------------------------------------------------------------------===//
-// ARC Utilities.
-//===----------------------------------------------------------------------===//
+/// @}
+///
+/// \defgroup ARCUtilities Utility declarations/definitions specific to ARC.
+/// @{
-#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.
+ /// \enum InstructionClass
+ /// \brief A simple classification for instructions.
enum InstructionClass {
IC_Retain, ///< objc_retain
IC_RetainRV, ///< objc_retainAutoreleasedReturnValue
};
}
-/// IsPotentialUse - Test whether the given value is possible a
-/// reference-counted pointer.
+/// \brief Test whether the given value is possible a reference-counted pointer.
static bool IsPotentialUse(const Value *Op) {
// Pointers to static or stack storage are not reference-counted pointers.
if (isa<Constant>(Op) || isa<AllocaInst>(Op))
return true;
}
-/// GetCallSiteClass - Helper for GetInstructionClass. Determines what kind
-/// of construct CS is.
+/// \brief Helper for GetInstructionClass. Determines what kind of construct CS
+/// is.
static InstructionClass GetCallSiteClass(ImmutableCallSite CS) {
for (ImmutableCallSite::arg_iterator I = CS.arg_begin(), E = CS.arg_end();
I != E; ++I)
return CS.onlyReadsMemory() ? IC_None : IC_Call;
}
-/// GetFunctionClass - Determine if F is one of the special known Functions.
-/// If it isn't, return IC_CallOrUser.
+/// \brief Determine if F is one of the special known Functions. If it isn't,
+/// return IC_CallOrUser.
static InstructionClass GetFunctionClass(const Function *F) {
Function::const_arg_iterator AI = F->arg_begin(), AE = F->arg_end();
return IC_CallOrUser;
}
-/// GetInstructionClass - Determine what kind of construct V is.
+/// \brief Determine what kind of construct V is.
static InstructionClass GetInstructionClass(const Value *V) {
if (const Instruction *I = dyn_cast<Instruction>(V)) {
// Any instruction other than bitcast and gep with a pointer operand have a
return IC_None;
}
-/// GetBasicInstructionClass - Determine what kind of construct V is. This is
-/// similar to GetInstructionClass except that it only detects objc runtine
-/// calls. This allows it to be faster.
+/// \brief Determine which objc runtime call instruction class V belongs to.
+///
+/// This is similar to GetInstructionClass except that it only detects objc
+/// runtime calls. This allows it to be faster.
+///
static InstructionClass GetBasicInstructionClass(const Value *V) {
if (const CallInst *CI = dyn_cast<CallInst>(V)) {
if (const Function *F = CI->getCalledFunction())
return isa<InvokeInst>(V) ? IC_CallOrUser : IC_User;
}
-/// IsRetain - Test if the given class is objc_retain or
-/// equivalent.
+/// \brief 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 given class is objc_autorelease or
-/// equivalent.
+/// \brief Test if the given class is objc_autorelease or equivalent.
static bool IsAutorelease(InstructionClass Class) {
return Class == IC_Autorelease ||
Class == IC_AutoreleaseRV;
}
-/// IsForwarding - Test if the given class represents instructions which return
-/// their argument verbatim.
+/// \brief Test if the given class represents instructions which return their
+/// argument verbatim.
static bool IsForwarding(InstructionClass Class) {
// objc_retainBlock technically doesn't always return its argument
// verbatim, but it doesn't matter for our purposes here.
Class == IC_NoopCast;
}
-/// IsNoopOnNull - Test if the given class represents instructions which do
-/// nothing if passed a null pointer.
+/// \brief Test if the given class represents instructions which do nothing if
+/// passed a null pointer.
static bool IsNoopOnNull(InstructionClass Class) {
return Class == IC_Retain ||
Class == IC_RetainRV ||
Class == IC_RetainBlock;
}
-/// IsAlwaysTail - Test if the given class represents instructions which are
-/// always safe to mark with the "tail" keyword.
+/// \brief Test if the given class represents instructions which are always safe
+/// to mark with the "tail" keyword.
static bool IsAlwaysTail(InstructionClass Class) {
// IC_RetainBlock may be given a stack argument.
return Class == IC_Retain ||
Class == IC_RetainRV ||
- Class == IC_Autorelease ||
Class == IC_AutoreleaseRV;
}
-/// IsNoThrow - Test if the given class represents instructions which are always
-/// safe to mark with the nounwind attribute..
+/// \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;
+}
+
+/// \brief Test if the given class represents instructions which are always safe
+/// to mark with the nounwind attribute.
static bool IsNoThrow(InstructionClass Class) {
// objc_retainBlock is not nounwind because it calls user copy constructors
// which could theoretically throw.
Class == IC_AutoreleasepoolPop;
}
-/// EraseInstruction - Erase the given instruction. Many ObjC calls return their
-/// argument verbatim, so if it's such a call and the return value has users,
-/// replace them with the argument value.
+/// \brief Erase the given instruction.
+///
+/// Many ObjC calls return their argument verbatim,
+/// so if it's such a call and the return value has users, replace them with the
+/// argument value.
+///
static void EraseInstruction(Instruction *CI) {
Value *OldArg = cast<CallInst>(CI)->getArgOperand(0);
RecursivelyDeleteTriviallyDeadInstructions(OldArg);
}
-/// GetUnderlyingObjCPtr - This is a wrapper around getUnderlyingObject which
-/// also knows how to look through objc_retain and objc_autorelease calls, which
-/// we know to return their argument verbatim.
+/// \brief This is a wrapper around getUnderlyingObject which also knows how to
+/// look through objc_retain and objc_autorelease calls, which we know to return
+/// their argument verbatim.
static const Value *GetUnderlyingObjCPtr(const Value *V) {
for (;;) {
V = GetUnderlyingObject(V);
return V;
}
-/// StripPointerCastsAndObjCCalls - This is a wrapper around
-/// Value::stripPointerCasts which also knows how to look through objc_retain
-/// and objc_autorelease calls, which we know to return their argument verbatim.
+/// \brief This is a wrapper around Value::stripPointerCasts which also knows
+/// how to look through objc_retain and objc_autorelease calls, which we know to
+/// return their argument verbatim.
static const Value *StripPointerCastsAndObjCCalls(const Value *V) {
for (;;) {
V = V->stripPointerCasts();
return V;
}
-/// StripPointerCastsAndObjCCalls - This is a wrapper around
-/// Value::stripPointerCasts which also knows how to look through objc_retain
-/// and objc_autorelease calls, which we know to return their argument verbatim.
+/// \brief This is a wrapper around Value::stripPointerCasts which also knows
+/// how to look through objc_retain and objc_autorelease calls, which we know to
+/// return their argument verbatim.
static Value *StripPointerCastsAndObjCCalls(Value *V) {
for (;;) {
V = V->stripPointerCasts();
return V;
}
-/// GetObjCArg - Assuming the given instruction is one of the special calls such
-/// as objc_retain or objc_release, return the argument value, stripped of no-op
+/// \brief Assuming the given instruction is one of the special calls such as
+/// objc_retain or objc_release, return the argument value, stripped of no-op
/// casts and forwarding calls.
static Value *GetObjCArg(Value *Inst) {
return StripPointerCastsAndObjCCalls(cast<CallInst>(Inst)->getArgOperand(0));
}
-/// IsObjCIdentifiedObject - This is similar to AliasAnalysis'
-/// isObjCIdentifiedObject, except that it uses special knowledge of
-/// ObjC conventions...
+/// \brief This is similar to AliasAnalysis's isObjCIdentifiedObject, except
+/// that it uses special knowledge of ObjC conventions.
static bool IsObjCIdentifiedObject(const Value *V) {
// Assume that call results and arguments have their own "provenance".
// Constants (including GlobalVariables) and Allocas are never
return false;
}
-/// FindSingleUseIdentifiedObject - This is similar to
-/// StripPointerCastsAndObjCCalls but it stops as soon as it finds a value
-/// with multiple uses.
+/// \brief This is similar to StripPointerCastsAndObjCCalls but it stops as soon
+/// as it finds a value with multiple uses.
static const Value *FindSingleUseIdentifiedObject(const Value *Arg) {
if (Arg->hasOneUse()) {
if (const BitCastInst *BC = dyn_cast<BitCastInst>(Arg))
return 0;
}
-/// ModuleHasARC - Test if the given module looks interesting to run ARC
-/// optimization on.
+/// \brief Test if the given module looks interesting to run ARC optimization
+/// on.
static bool ModuleHasARC(const Module &M) {
return
M.getNamedValue("objc_retain") ||
M.getNamedValue("objc_unretainedPointer");
}
-/// DoesObjCBlockEscape - Test whether the given pointer, which is an
-/// Objective C block pointer, does not "escape". This differs from regular
-/// escape analysis in that a use as an argument to a call is not considered
-/// an escape.
+/// \brief Test whether the given pointer, which is an Objective C block
+/// pointer, does not "escape".
+///
+/// This differs from regular escape analysis in that a use as an
+/// argument to a call is not considered an escape.
+///
static bool DoesObjCBlockEscape(const Value *BlockPtr) {
+
+ DEBUG(dbgs() << "DoesObjCBlockEscape: Target: " << *BlockPtr << "\n");
+
// Walk the def-use chains.
SmallVector<const Value *, 4> Worklist;
Worklist.push_back(BlockPtr);
+
+ // Ensure we do not visit any value twice.
+ SmallPtrSet<const Value *, 4> VisitedSet;
+
do {
const Value *V = Worklist.pop_back_val();
+
+ DEBUG(dbgs() << "DoesObjCBlockEscape: 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() << "DoesObjCBlockEscape: User: " << *UUser << "\n");
+
// Special - Use by a call (callee or argument) is not considered
// to be an escape.
switch (GetBasicInstructionClass(UUser)) {
case IC_InitWeak:
case IC_StoreStrong:
case IC_Autorelease:
- case IC_AutoreleaseRV:
+ case IC_AutoreleaseRV: {
+ DEBUG(dbgs() << "DoesObjCBlockEscape: User copies pointer arguments. "
+ "Block Escapes!\n");
// These special functions make copies of their pointer arguments.
return true;
+ }
case IC_User:
case IC_None:
// Use by an instruction which copies the value is an escape if the
// result is an escape.
if (isa<BitCastInst>(UUser) || isa<GetElementPtrInst>(UUser) ||
isa<PHINode>(UUser) || isa<SelectInst>(UUser)) {
- Worklist.push_back(UUser);
+
+ if (!VisitedSet.insert(UUser)) {
+ DEBUG(dbgs() << "DoesObjCBlockEscape: User copies value. Escapes "
+ "if result escapes. Adding to list.\n");
+ Worklist.push_back(UUser);
+ } else {
+ DEBUG(dbgs() << "DoesObjCBlockEscape: Already visited node.\n");
+ }
continue;
}
// Use by a load is not an escape.
continue;
}
// Otherwise, conservatively assume an escape.
+ DEBUG(dbgs() << "DoesObjCBlockEscape: Assuming block escapes.\n");
return true;
}
} while (!Worklist.empty());
// No escapes found.
+ DEBUG(dbgs() << "DoesObjCBlockEscape: Block does not escape.\n");
return false;
}
-//===----------------------------------------------------------------------===//
-// ARC AliasAnalysis.
-//===----------------------------------------------------------------------===//
+/// @}
+///
+/// \defgroup ARCAA Extends alias analysis using ObjC specific knowledge.
+/// @{
-#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
- /// implementation that uses knowledge of ARC constructs to answer queries.
+ /// \brief This is a simple alias analysis implementation that uses knowledge
+ /// of ARC constructs to answer queries.
///
/// TODO: This class could be generalized to know about other ObjC-specific
/// tricks. Such as knowing that ivars in the non-fragile ABI are non-aliasing
InitializeAliasAnalysis(this);
}
- /// getAdjustedAnalysisPointer - This method is used when a pass implements
- /// an analysis interface through multiple inheritance. If needed, it
- /// should override this to adjust the this pointer as needed for the
- /// specified pass info.
+ /// This method is used when a pass implements an analysis interface through
+ /// multiple inheritance. If needed, it should override this to adjust the
+ /// this pointer as needed for the specified pass info.
virtual void *getAdjustedAnalysisPointer(const void *PI) {
if (PI == &AliasAnalysis::ID)
return static_cast<AliasAnalysis *>(this);
return AliasAnalysis::getModRefInfo(CS1, CS2);
}
-//===----------------------------------------------------------------------===//
-// ARC expansion.
-//===----------------------------------------------------------------------===//
+/// @}
+///
+/// \defgroup ARCExpansion Early ARC Optimizations.
+/// @{
#include "llvm/Support/InstIterator.h"
#include "llvm/Transforms/Scalar.h"
namespace {
- /// ObjCARCExpand - Early ARC transformations.
+ /// \brief Early ARC transformations.
class ObjCARCExpand : public FunctionPass {
virtual void getAnalysisUsage(AnalysisUsage &AU) const;
virtual bool doInitialization(Module &M);
virtual bool runOnFunction(Function &F);
- /// Run - A flag indicating whether this optimization pass should run.
+ /// A flag indicating whether this optimization pass should run.
bool Run;
public:
bool Changed = false;
+ DEBUG(dbgs() << "ObjCARCExpand: Visiting Function: " << F.getName() << "\n");
+
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.
-//===----------------------------------------------------------------------===//
+/// @}
+///
+/// \defgroup ARCAPElim ARC Autorelease Pool Elimination.
+/// @{
-#include "llvm/Constants.h"
#include "llvm/ADT/STLExtras.h"
+#include "llvm/IR/Constants.h"
namespace {
- /// ObjCARCAPElim - Autorelease pool elimination.
+ /// \brief Autorelease pool elimination.
class ObjCARCAPElim : public ModulePass {
virtual void getAnalysisUsage(AnalysisUsage &AU) const;
virtual bool runOnModule(Module &M);
AU.setPreservesCFG();
}
-/// MayAutorelease - Interprocedurally determine if calls made by the
-/// given call site can possibly produce autoreleases.
+/// Interprocedurally determine if calls made by the given call site can
+/// possibly produce autoreleases.
bool ObjCARCAPElim::MayAutorelease(ImmutableCallSite CS, unsigned Depth) {
if (const Function *Callee = CS.getCalledFunction()) {
if (Callee->isDeclaration() || Callee->mayBeOverridden())
// 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();
}
return Changed;
}
-//===----------------------------------------------------------------------===//
-// ARC optimization.
-//===----------------------------------------------------------------------===//
+/// @}
+///
+/// \defgroup ARCOpt ARC Optimization.
+/// @{
// TODO: On code like this:
//
// 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");
STATISTIC(NumPeeps, "Number of calls peephole-optimized");
namespace {
- /// ProvenanceAnalysis - This is similar to BasicAliasAnalysis, and it
- /// uses many of the same techniques, except it uses special ObjC-specific
- /// reasoning about pointer relationships.
+ /// \brief This is similar to BasicAliasAnalysis, and it uses many of the same
+ /// techniques, except it uses special ObjC-specific reasoning about pointer
+ /// relationships.
class ProvenanceAnalysis {
AliasAnalysis *AA;
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() {}
return false;
}
-/// isStoredObjCPointer - Test if the value of P, or any value covered by its
-/// provenance, is ever stored within the function (not counting callees).
+/// Test if the value of P, or any value covered by its provenance, is ever
+/// stored within the function (not counting callees).
static bool isStoredObjCPointer(const Value *P) {
SmallPtrSet<const Value *, 8> Visited;
SmallVector<const Value *, 8> Worklist;
}
namespace {
- // Sequence - A sequence of states that a pointer may go through in which an
- // objc_retain and objc_release are actually needed.
+ /// \enum Sequence
+ ///
+ /// \brief A sequence of states that a pointer may go through in which an
+ /// objc_retain and objc_release are actually needed.
enum Sequence {
S_None,
S_Retain, ///< objc_retain(x)
}
namespace {
- /// RRInfo - Unidirectional information about either a
+ /// \brief Unidirectional information about either a
/// retain-decrement-use-release sequence or release-use-decrement-retain
/// reverese sequence.
struct RRInfo {
- /// KnownSafe - After an objc_retain, the reference count of the referenced
+ /// After an objc_retain, the reference count of the referenced
/// object is known to be positive. Similarly, before an objc_release, the
/// reference count of the referenced object is known to be positive. If
/// there are retain-release pairs in code regions where the retain count
/// KnownSafe is true when either of these conditions is satisfied.
bool KnownSafe;
- /// IsRetainBlock - True if the Calls are objc_retainBlock calls (as
- /// opposed to objc_retain calls).
+ /// True if the Calls are objc_retainBlock calls (as opposed to objc_retain
+ /// calls).
bool IsRetainBlock;
- /// IsTailCallRelease - True of the objc_release calls are all marked
- /// with the "tail" keyword.
+ /// True of the objc_release calls are all marked with the "tail" keyword.
bool IsTailCallRelease;
- /// ReleaseMetadata - If the Calls are objc_release calls and they all have
- /// a clang.imprecise_release tag, this is the metadata tag.
+ /// If the Calls are objc_release calls and they all have a
+ /// clang.imprecise_release tag, this is the metadata tag.
MDNode *ReleaseMetadata;
- /// Calls - For a top-down sequence, the set of objc_retains or
+ /// For a top-down sequence, the set of objc_retains or
/// objc_retainBlocks. For bottom-up, the set of objc_releases.
SmallPtrSet<Instruction *, 2> Calls;
- /// ReverseInsertPts - The set of optimal insert positions for
- /// moving calls in the opposite sequence.
+ /// The set of optimal insert positions for moving calls in the opposite
+ /// sequence.
SmallPtrSet<Instruction *, 2> ReverseInsertPts;
RRInfo() :
}
namespace {
- /// PtrState - This class summarizes several per-pointer runtime properties
- /// which are propogated through the flow graph.
+ /// \brief This class summarizes several per-pointer runtime properties which
+ /// are propogated through the flow graph.
class PtrState {
- /// KnownPositiveRefCount - True if the reference count is known to
- /// be incremented.
+ /// True if the reference count is known to be incremented.
bool KnownPositiveRefCount;
- /// Partial - True of 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.
+ /// True of 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;
- /// Seq - The current position in the sequence.
+ /// The current position in the sequence.
Sequence Seq : 8;
public:
- /// RRI - Unidirectional information about the current sequence.
+ /// Unidirectional information about the current sequence.
+ ///
/// TODO: Encapsulate this better.
RRInfo RRI;
}
namespace {
- /// BBState - Per-BasicBlock state.
+ /// \brief Per-BasicBlock state.
class BBState {
- /// TopDownPathCount - The number of unique control paths from the entry
- /// which can reach this block.
+ /// The number of unique control paths from the entry which can reach this
+ /// block.
unsigned TopDownPathCount;
- /// BottomUpPathCount - The number of unique control paths to exits
- /// from this block.
+ /// The number of unique control paths to exits from this block.
unsigned BottomUpPathCount;
- /// MapTy - A type for PerPtrTopDown and PerPtrBottomUp.
+ /// A type for PerPtrTopDown and PerPtrBottomUp.
typedef MapVector<const Value *, PtrState> MapTy;
- /// PerPtrTopDown - The top-down traversal uses this to record information
- /// known about a pointer at the bottom of each block.
+ /// The top-down traversal uses this to record information known about a
+ /// pointer at the bottom of each block.
MapTy PerPtrTopDown;
- /// PerPtrBottomUp - The bottom-up traversal uses this to record information
- /// known about a pointer at the top of each block.
+ /// The bottom-up traversal uses this to record information known about a
+ /// pointer at the top of each block.
MapTy PerPtrBottomUp;
- /// Preds, Succs - Effective successors and predecessors of the current
- /// block (this ignores ignorable edges and ignored backedges).
+ /// Effective predecessors of the current block ignoring ignorable edges and
+ /// ignored backedges.
SmallVector<BasicBlock *, 2> Preds;
+ /// Effective successors of the current block ignoring ignorable edges and
+ /// ignored backedges.
SmallVector<BasicBlock *, 2> Succs;
public:
return PerPtrBottomUp.end();
}
- /// SetAsEntry - Mark this block as being an entry block, which has one
- /// path from the entry by definition.
+ /// Mark this block as being an entry block, which has one path from the
+ /// entry by definition.
void SetAsEntry() { TopDownPathCount = 1; }
- /// SetAsExit - Mark this block as being an exit block, which has one
- /// path to an exit by definition.
+ /// Mark this block as being an exit block, which has one path to an exit by
+ /// definition.
void SetAsExit() { BottomUpPathCount = 1; }
PtrState &getPtrTopDownState(const Value *Arg) {
void MergePred(const BBState &Other);
void MergeSucc(const BBState &Other);
- /// GetAllPathCount - Return the number of possible unique paths from an
- /// entry to an exit which pass through this block. This is only valid
- /// after both the top-down and bottom-up traversals are complete.
+ /// Return the number of possible unique paths from an entry to an exit
+ /// which pass through this block. This is only valid after both the
+ /// top-down and bottom-up traversals are complete.
unsigned GetAllPathCount() const {
assert(TopDownPathCount != 0);
assert(BottomUpPathCount != 0);
BottomUpPathCount = Other.BottomUpPathCount;
}
-/// MergePred - The top-down traversal uses this to merge information about
-/// predecessors to form the initial state for a new block.
+/// The top-down traversal uses this to merge information about predecessors to
+/// form the initial state for a new block.
void BBState::MergePred(const BBState &Other) {
// Other.TopDownPathCount can be 0, in which case it is either dead or a
// loop backedge. Loop backedges are special.
TopDownPathCount += Other.TopDownPathCount;
- // Check for overflow. If we have overflow, fall back to conservative behavior.
+ // Check for overflow. If we have overflow, fall back to conservative
+ // behavior.
if (TopDownPathCount < Other.TopDownPathCount) {
clearTopDownPointers();
return;
MI->second.Merge(PtrState(), /*TopDown=*/true);
}
-/// MergeSucc - The bottom-up traversal uses this to merge information about
-/// successors to form the initial state for a new block.
+/// The bottom-up traversal uses this to merge information about successors to
+/// form the initial state for a new block.
void BBState::MergeSucc(const BBState &Other) {
// Other.BottomUpPathCount can be 0, in which case it is either dead or a
// loop backedge. Loop backedges are special.
BottomUpPathCount += Other.BottomUpPathCount;
- // Check for overflow. If we have overflow, fall back to conservative behavior.
+ // Check for overflow. If we have overflow, fall back to conservative
+ // behavior.
if (BottomUpPathCount < Other.BottomUpPathCount) {
clearBottomUpPointers();
return;
}
namespace {
- /// ObjCARCOpt - The main ARC optimization pass.
+ /// \brief The main ARC optimization pass.
class ObjCARCOpt : public FunctionPass {
bool Changed;
ProvenanceAnalysis PA;
- /// Run - A flag indicating whether this optimization pass should run.
+ /// A flag indicating whether this optimization pass should run.
bool Run;
- /// RetainRVCallee, etc. - 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.
- Constant *RetainRVCallee, *AutoreleaseRVCallee, *ReleaseCallee,
- *RetainCallee, *RetainBlockCallee, *AutoreleaseCallee;
-
- /// UsedInThisFunciton - Flags which determine whether each of the
- /// interesting runtine functions is in fact used in the current function.
+ /// 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_retainAutoreleasedReturnValue.
+ Constant *RetainRVCallee;
+ /// 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;
- /// ImpreciseReleaseMDKind - The Metadata Kind for clang.imprecise_release
- /// metadata.
+ /// The Metadata Kind for clang.imprecise_release metadata.
unsigned ImpreciseReleaseMDKind;
- /// CopyOnEscapeMDKind - The Metadata Kind for clang.arc.copy_on_escape
- /// metadata.
+ /// The Metadata Kind for clang.arc.copy_on_escape metadata.
unsigned CopyOnEscapeMDKind;
- /// NoObjCARCExceptionsMDKind - The Metadata Kind for
- /// clang.arc.no_objc_arc_exceptions metadata.
+ /// The Metadata Kind for clang.arc.no_objc_arc_exceptions metadata.
unsigned NoObjCARCExceptionsMDKind;
Constant *getRetainRVCallee(Module *M);
void OptimizeRetainCall(Function &F, Instruction *Retain);
bool OptimizeRetainRVCall(Function &F, Instruction *RetainRV);
- void OptimizeAutoreleaseRVCall(Function &F, Instruction *AutoreleaseRV);
+ void OptimizeAutoreleaseRVCall(Function &F, Instruction *AutoreleaseRV,
+ InstructionClass &Class);
void OptimizeIndividualCalls(Function &F);
void CheckForCFGHazards(const BasicBlock *BB,
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.
+/// 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 true;
}
-/// CanAlterRefCount - Test whether the given instruction can result in a
-/// reference count modification (positive or negative) for the pointer's
-/// object.
+/// Test whether the given instruction can result in a reference count
+/// modification (positive or negative) for the pointer's object.
static bool
CanAlterRefCount(const Instruction *Inst, const Value *Ptr,
ProvenanceAnalysis &PA, InstructionClass Class) {
return true;
}
-/// CanUse - Test whether the given instruction can "use" the given pointer's
-/// object in a way that requires the reference count to be positive.
+/// Test whether the given instruction can "use" the given pointer's object in a
+/// way that requires the reference count to be positive.
static bool
CanUse(const Instruction *Inst, const Value *Ptr, ProvenanceAnalysis &PA,
InstructionClass Class) {
return false;
}
-/// CanInterruptRV - Test whether the given instruction can autorelease
-/// any pointer or cause an autoreleasepool pop.
+/// Test whether the given instruction can autorelease any pointer or cause an
+/// autoreleasepool pop.
static bool
CanInterruptRV(InstructionClass Class) {
switch (Class) {
}
namespace {
- /// DependenceKind - There are several kinds of dependence-like concepts in
- /// use here.
+ /// \enum DependenceKind
+ /// \brief Defines different dependence kinds among various ARC constructs.
+ ///
+ /// There are several kinds of dependence-like concepts in use here.
+ ///
enum DependenceKind {
NeedsPositiveRetainCount,
AutoreleasePoolBoundary,
};
}
-/// Depends - Test if there can be dependencies on Inst through Arg. This
-/// function only tests dependencies relevant for removing pairs of calls.
+/// Test if there can be dependencies on Inst through Arg. This function only
+/// tests dependencies relevant for removing pairs of calls.
static bool
Depends(DependenceKind Flavor, Instruction *Inst, const Value *Arg,
ProvenanceAnalysis &PA) {
llvm_unreachable("Invalid dependence flavor");
}
-/// FindDependencies - Walk up the CFG from StartPos (which is in StartBB) and
-/// find local and non-local dependencies on Arg.
+/// Walk up the CFG from StartPos (which is in StartBB) and find local and
+/// non-local dependencies on Arg.
+///
/// TODO: Cache results?
static void
FindDependencies(DependenceKind Flavor,
cast<GetElementPtrInst>(I)->hasAllZeroIndices());
}
-/// OptimizeRetainCall - Turn objc_retain into
-/// objc_retainAutoreleasedReturnValue if the operand is a return value.
+/// Turn objc_retain into objc_retainAutoreleasedReturnValue if the operand is a
+/// return value.
void
ObjCARCOpt::OptimizeRetainCall(Function &F, Instruction *Retain) {
ImmutableCallSite CS(GetObjCArg(Retain));
// Turn it to an objc_retainAutoreleasedReturnValue..
Changed = true;
++NumPeeps;
+
+ DEBUG(dbgs() << "ObjCARCOpt::OptimizeRetainCall: Transforming "
+ "objc_retain => objc_retainAutoreleasedReturnValue"
+ " since the operand is a return value.\n"
+ " Old: "
+ << *Retain << "\n");
+
cast<CallInst>(Retain)->setCalledFunction(getRetainRVCallee(F.getParent()));
+
+ DEBUG(dbgs() << " New: "
+ << *Retain << "\n");
}
-/// OptimizeRetainRVCall - 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.
+/// 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.
bool
ObjCARCOpt::OptimizeRetainRVCall(Function &F, Instruction *RetainRV) {
// Check for the argument being from an immediately preceding call or invoke.
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;
}
-/// OptimizeAutoreleaseRVCall - Turn objc_autoreleaseReturnValue into
-/// objc_autorelease if the result is not used as a return value.
+/// Turn objc_autoreleaseReturnValue into objc_autorelease if the result is not
+/// used as a return value.
void
-ObjCARCOpt::OptimizeAutoreleaseRVCall(Function &F, Instruction *AutoreleaseRV) {
+ObjCARCOpt::OptimizeAutoreleaseRVCall(Function &F, Instruction *AutoreleaseRV,
+ InstructionClass &Class) {
// Check for a return of the pointer value.
const Value *Ptr = GetObjCArg(AutoreleaseRV);
SmallVector<const Value *, 2> Users;
Changed = true;
++NumPeeps;
- cast<CallInst>(AutoreleaseRV)->
+
+ DEBUG(dbgs() << "ObjCARCOpt::OptimizeAutoreleaseRVCall: Transforming "
+ "objc_autoreleaseReturnValue => "
+ "objc_autorelease since its operand is not used as a return "
+ "value.\n"
+ " Old: "
+ << *AutoreleaseRV << "\n");
+
+ CallInst *AutoreleaseRVCI = cast<CallInst>(AutoreleaseRV);
+ AutoreleaseRVCI->
setCalledFunction(getAutoreleaseCallee(F.getParent()));
+ AutoreleaseRVCI->setTailCall(false); // Never tail call objc_autorelease.
+ Class = IC_Autorelease;
+
+ DEBUG(dbgs() << " New: "
+ << *AutoreleaseRV << "\n");
+
}
-/// OptimizeIndividualCalls - Visit each call, one at a time, and make
-/// simplifications without doing any additional analysis.
+/// Visit each call, one at a time, and make simplifications without doing any
+/// additional analysis.
void ObjCARCOpt::OptimizeIndividualCalls(Function &F) {
// Reset all the flags in preparation for recomputing them.
UsedInThisFunction = 0;
// 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;
new StoreInst(UndefValue::get(cast<PointerType>(Ty)->getElementType()),
Constant::getNullValue(Ty),
CI);
- CI->replaceAllUsesWith(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");
+ CI->replaceAllUsesWith(NewValue);
CI->eraseFromParent();
continue;
}
new StoreInst(UndefValue::get(cast<PointerType>(Ty)->getElementType()),
Constant::getNullValue(Ty),
CI);
- CI->replaceAllUsesWith(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");
+
+ CI->replaceAllUsesWith(NewValue);
CI->eraseFromParent();
continue;
}
continue;
break;
case IC_AutoreleaseRV:
- OptimizeAutoreleaseRVCall(F, Inst);
+ OptimizeAutoreleaseRVCall(F, Inst, Class);
break;
}
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;
// 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");
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;
}
}
} while (!Worklist.empty());
}
+ DEBUG(dbgs() << "ObjCARCOpt::OptimizeIndividualCalls: Finished List.\n");
}
-/// CheckForCFGHazards - Check for critical edges, loop boundaries, irreducible
-/// control flow, or other CFG structures where moving code across the edge
-/// would result in it being executed more.
+/// Check for critical edges, loop boundaries, irreducible control flow, or
+/// other CFG structures where moving code across the edge would result in it
+/// being executed more.
void
ObjCARCOpt::CheckForCFGHazards(const BasicBlock *BB,
DenseMap<const BasicBlock *, BBState> &BBStates,
// If the terminator is an invoke marked with the
// clang.arc.no_objc_arc_exceptions metadata, the unwind edge can be
// ignored, for ARC purposes.
- if (isa<InvokeInst>(TI) && TI->getMetadata(NoObjCARCExceptionsMDKind))
+ if (isa<InvokeInst>(TI) && TI->getMetadata(NoObjCARCExceptionsMDKind)) {
+ DEBUG(dbgs() << "ObjCARCOpt::CheckForCFGHazards: Found an invoke "
+ "terminator marked with "
+ "clang.arc.no_objc_arc_exceptions. Ignoring unwind "
+ "edge.\n");
--SE;
+ }
for (; SI != SE; ++SI) {
Sequence SuccSSeq = S_None;
// If the terminator is an invoke marked with the
// clang.arc.no_objc_arc_exceptions metadata, the unwind edge can be
// ignored, for ARC purposes.
- if (isa<InvokeInst>(TI) && TI->getMetadata(NoObjCARCExceptionsMDKind))
+ if (isa<InvokeInst>(TI) && TI->getMetadata(NoObjCARCExceptionsMDKind)) {
+ DEBUG(dbgs() << "ObjCARCOpt::CheckForCFGHazards: Found an invoke "
+ "terminator marked with "
+ "clang.arc.no_objc_arc_exceptions. Ignoring unwind "
+ "edge.\n");
--SE;
+ }
for (; SI != SE; ++SI) {
Sequence SuccSSeq = S_None;
// Theoretically we could implement removal of nested retain+release
// 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)
+ if (S.GetSeq() == S_Release || S.GetSeq() == S_MovableRelease) {
+ DEBUG(dbgs() << "ObjCARCOpt::VisitInstructionBottomUp: Found nested "
+ "releases (i.e. a release pair)\n");
NestingDetected = true;
+ }
MDNode *ReleaseMetadata = Inst->getMetadata(ImpreciseReleaseMDKind);
S.ResetSequenceProgress(ReleaseMetadata ? S_MovableRelease : S_Release);
if (isa<InvokeInst>(Inst))
continue;
+ DEBUG(dbgs() << "ObjCARCOpt::VisitButtonUp: Visiting " << *Inst << "\n");
+
NestingDetected |= VisitInstructionBottomUp(Inst, BB, Retains, MyStates);
}
// 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");
+
NestingDetected |= VisitInstructionTopDown(Inst, Releases, MyStates);
}
SmallVectorImpl<BasicBlock *> &ReverseCFGPostOrder,
unsigned NoObjCARCExceptionsMDKind,
DenseMap<const BasicBlock *, BBState> &BBStates) {
- /// Visited - The visited set, for doing DFS walks.
+ /// The visited set, for doing DFS walks.
SmallPtrSet<BasicBlock *, 16> Visited;
// Do DFS, computing the PostOrder.
// If the terminator is an invoke marked with the
// clang.arc.no_objc_arc_exceptions metadata, the unwind edge can be
// ignored, for ARC purposes.
- if (isa<InvokeInst>(TI) && TI->getMetadata(NoObjCARCExceptionsMDKind))
+ if (isa<InvokeInst>(TI) && TI->getMetadata(NoObjCARCExceptionsMDKind)) {
+ DEBUG(dbgs() << "ObjCARCOpt::ComputePostOrders: Found an invoke "
+ "terminator marked with "
+ "clang.arc.no_objc_arc_exceptions. Ignoring unwind "
+ "edge.\n");
--SE;
+ }
while (SuccStack.back().second != SE) {
BasicBlock *SuccBB = *SuccStack.back().second++;
}
}
-// Visit - Visit the function both top-down and bottom-up.
+// Visit the function both top-down and bottom-up.
bool
ObjCARCOpt::Visit(Function &F,
DenseMap<const BasicBlock *, BBState> &BBStates,
return TopDownNestingDetected && BottomUpNestingDetected;
}
-/// MoveCalls - Move the calls in RetainsToMove and ReleasesToMove.
+/// Move the calls in RetainsToMove and ReleasesToMove.
void ObjCARCOpt::MoveCalls(Value *Arg,
RRInfo &RetainsToMove,
RRInfo &ReleasesToMove,
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");
}
}
-/// PerformCodePlacement - Identify pairings between the retains and releases,
-/// and delete and/or move them.
+/// Identify pairings between the retains and releases, and delete and/or move
+/// them.
bool
ObjCARCOpt::PerformCodePlacement(DenseMap<const BasicBlock *, BBState>
&BBStates,
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
return AnyPairsCompletelyEliminated;
}
-/// OptimizeWeakCalls - Weak pointer optimizations.
+/// Weak pointer optimizations.
void ObjCARCOpt::OptimizeWeakCalls(Function &F) {
// 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");
+
InstructionClass Class = GetBasicInstructionClass(Inst);
if (Class != IC_LoadWeak && Class != IC_LoadWeakRetained)
continue;
done:;
}
}
+
+ DEBUG(dbgs() << "ObjCARCOpt::OptimizeWeakCalls: Finished List.\n\n");
+
}
-/// OptimizeSequences - Identify program paths which execute sequences of
-/// retains and releases which can be eliminated.
+/// Identify program paths which execute sequences of retains and releases which
+/// can be eliminated.
bool ObjCARCOpt::OptimizeSequences(Function &F) {
/// Releases, Retains - These are used to store the results of the main flow
/// analysis. These use Value* as the key instead of Instruction* so that the
DenseMap<Value *, RRInfo> Releases;
MapVector<Value *, RRInfo> Retains;
- /// BBStates, This is used during the traversal of the function to track the
+ /// This is used during the traversal of the function to track the
/// states for each identified object at each block.
DenseMap<const BasicBlock *, BBState> BBStates;
NestingDetected;
}
-/// OptimizeReturns - Look for this pattern:
-///
+/// 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));
// 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 = false;
+ DEBUG(dbgs() << "ObjCARCOpt: Visiting Function: " << F.getName() << "\n");
+
PA.setAA(&getAnalysis<AliasAnalysis>());
// This pass performs several distinct transformations. As a compile-time aid
(1 << IC_AutoreleaseRV)))
OptimizeReturns(F);
+ DEBUG(dbgs() << "\n");
+
return Changed;
}
PA.clear();
}
-//===----------------------------------------------------------------------===//
-// ARC contraction.
-//===----------------------------------------------------------------------===//
+/// @}
+///
+/// \defgroup ARCContract ARC Contraction.
+/// @{
// 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");
namespace {
- /// ObjCARCContract - Late ARC optimizations. These change the IR in a way
- /// that makes it difficult to be analyzed by ObjCARCOpt, so it's run late.
+ /// \brief Late ARC optimizations
+ ///
+ /// These change the IR in a way that makes it difficult to be analyzed by
+ /// ObjCARCOpt, so it's run late.
class ObjCARCContract : public FunctionPass {
bool Changed;
AliasAnalysis *AA;
DominatorTree *DT;
ProvenanceAnalysis PA;
- /// Run - A flag indicating whether this optimization pass should run.
+ /// A flag indicating whether this optimization pass should run.
bool Run;
- /// StoreStrongCallee, etc. - 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.
- Constant *StoreStrongCallee,
- *RetainAutoreleaseCallee, *RetainAutoreleaseRVCallee;
+ /// 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_storeStrong().
+ Constant *StoreStrongCallee;
+ /// Declaration for objc_retainAutorelease().
+ Constant *RetainAutoreleaseCallee;
+ /// Declaration for objc_retainAutoreleaseReturnValue().
+ Constant *RetainAutoreleaseRVCallee;
- /// RetainRVMarker - The inline asm string to insert between calls and
- /// RetainRV calls to make the optimization work on targets which need it.
+ /// The inline asm string to insert between calls and RetainRV calls to make
+ /// the optimization work on targets which need it.
const MDString *RetainRVMarker;
- /// StoreStrongCalls - The set of inserted objc_storeStrong calls. If
- /// at the end of walking the function we have found no alloca
- /// instructions, these calls can be marked "tail".
+ /// The set of inserted objc_storeStrong calls. If at the end of walking the
+ /// function we have found no alloca instructions, these calls can be marked
+ /// "tail".
SmallPtrSet<CallInst *, 8> StoreStrongCalls;
Constant *getStoreStrongCallee(Module *M);
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;
}
-/// ContractAutorelease - Merge an autorelease with a retain into a fused call.
+/// Merge an autorelease with a retain into a fused call.
bool
ObjCARCContract::ContractAutorelease(Function &F, Instruction *Autorelease,
InstructionClass Class,
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;
}
-/// ContractRelease - Attempt to merge an objc_release with a store, load, and
-/// objc_retain to form an objc_storeStrong. This can be a little tricky because
-/// the instructions don't always appear in order, and there may be unrelated
-/// intervening instructions.
+/// Attempt to merge an objc_release with a store, load, and objc_retain to form
+/// an objc_storeStrong. This can be a little tricky because the instructions
+/// don't always appear in order, and there may be unrelated intervening
+/// instructions.
void ObjCARCContract::ContractRelease(Instruction *Release,
inst_iterator &Iter) {
LoadInst *Load = dyn_cast<LoadInst>(GetObjCArg(Release));
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);
} 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()),
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);
return Changed;
}
+
+/// @}
+///