//===- DependencyAnalysis.cpp - ObjC ARC Optimization ---------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// /// \file /// /// This file defines special dependency analysis routines used in Objective C /// ARC Optimizations. /// /// 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. /// //===----------------------------------------------------------------------===// #include "ObjCARC.h" #include "DependencyAnalysis.h" #include "ProvenanceAnalysis.h" #include "llvm/IR/CFG.h" using namespace llvm; using namespace llvm::objcarc; #define DEBUG_TYPE "objc-arc-dependency" /// Test whether the given instruction can result in a reference count /// modification (positive or negative) for the pointer's object. bool llvm::objcarc::CanAlterRefCount(const Instruction *Inst, const Value *Ptr, ProvenanceAnalysis &PA, ARCInstKind Class) { switch (Class) { case ARCInstKind::Autorelease: case ARCInstKind::AutoreleaseRV: case ARCInstKind::IntrinsicUser: case ARCInstKind::User: // These operations never directly modify a reference count. return false; default: break; } ImmutableCallSite CS(Inst); assert(CS && "Only calls can alter reference counts!"); // See if AliasAnalysis can help us with the call. AliasAnalysis::ModRefBehavior MRB = PA.getAA()->getModRefBehavior(CS); if (AliasAnalysis::onlyReadsMemory(MRB)) return false; if (AliasAnalysis::onlyAccessesArgPointees(MRB)) { const DataLayout &DL = Inst->getModule()->getDataLayout(); for (ImmutableCallSite::arg_iterator I = CS.arg_begin(), E = CS.arg_end(); I != E; ++I) { const Value *Op = *I; if (IsPotentialRetainableObjPtr(Op, *PA.getAA()) && PA.related(Ptr, Op, DL)) return true; } return false; } // Assume the worst. return true; } bool llvm::objcarc::CanDecrementRefCount(const Instruction *Inst, const Value *Ptr, ProvenanceAnalysis &PA, ARCInstKind Class) { // First perform a quick check if Class can not touch ref counts. if (!CanDecrementRefCount(Class)) return false; // Otherwise, just use CanAlterRefCount for now. return CanAlterRefCount(Inst, Ptr, PA, Class); } /// Test whether the given instruction can "use" the given pointer's object in a /// way that requires the reference count to be positive. bool llvm::objcarc::CanUse(const Instruction *Inst, const Value *Ptr, ProvenanceAnalysis &PA, ARCInstKind Class) { // ARCInstKind::Call operations (as opposed to // ARCInstKind::CallOrUser) never "use" objc pointers. if (Class == ARCInstKind::Call) return false; const DataLayout &DL = Inst->getModule()->getDataLayout(); // Consider various instructions which may have pointer arguments which are // not "uses". if (const ICmpInst *ICI = dyn_cast(Inst)) { // 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 (!IsPotentialRetainableObjPtr(ICI->getOperand(1), *PA.getAA())) return false; } else if (auto CS = ImmutableCallSite(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 (IsPotentialRetainableObjPtr(Op, *PA.getAA()) && PA.related(Ptr, Op, DL)) return true; } return false; } else if (const StoreInst *SI = dyn_cast(Inst)) { // Special-case stores, because we don't care about the stored value, just // the store address. const Value *Op = GetUnderlyingObjCPtr(SI->getPointerOperand(), DL); // If we can't tell what the underlying object was, assume there is a // dependence. return IsPotentialRetainableObjPtr(Op, *PA.getAA()) && PA.related(Op, Ptr, DL); } // 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 (IsPotentialRetainableObjPtr(Op, *PA.getAA()) && PA.related(Ptr, Op, DL)) return true; } return false; } /// Test if there can be dependencies on Inst through Arg. This function only /// tests dependencies relevant for removing pairs of calls. bool llvm::objcarc::Depends(DependenceKind Flavor, Instruction *Inst, const Value *Arg, ProvenanceAnalysis &PA) { // If we've reached the definition of Arg, stop. if (Inst == Arg) return true; switch (Flavor) { case NeedsPositiveRetainCount: { ARCInstKind Class = GetARCInstKind(Inst); switch (Class) { case ARCInstKind::AutoreleasepoolPop: case ARCInstKind::AutoreleasepoolPush: case ARCInstKind::None: return false; default: return CanUse(Inst, Arg, PA, Class); } } case AutoreleasePoolBoundary: { ARCInstKind Class = GetARCInstKind(Inst); switch (Class) { case ARCInstKind::AutoreleasepoolPop: case ARCInstKind::AutoreleasepoolPush: // These mark the end and begin of an autorelease pool scope. return true; default: // Nothing else does this. return false; } } case CanChangeRetainCount: { ARCInstKind Class = GetARCInstKind(Inst); switch (Class) { case ARCInstKind::AutoreleasepoolPop: // Conservatively assume this can decrement any count. return true; case ARCInstKind::AutoreleasepoolPush: case ARCInstKind::None: return false; default: return CanAlterRefCount(Inst, Arg, PA, Class); } } case RetainAutoreleaseDep: switch (GetBasicARCInstKind(Inst)) { case ARCInstKind::AutoreleasepoolPop: case ARCInstKind::AutoreleasepoolPush: // Don't merge an objc_autorelease with an objc_retain inside a different // autoreleasepool scope. return true; case ARCInstKind::Retain: case ARCInstKind::RetainRV: // Check for a retain of the same pointer for merging. return GetArgRCIdentityRoot(Inst) == Arg; default: // Nothing else matters for objc_retainAutorelease formation. return false; } case RetainAutoreleaseRVDep: { ARCInstKind Class = GetBasicARCInstKind(Inst); switch (Class) { case ARCInstKind::Retain: case ARCInstKind::RetainRV: // Check for a retain of the same pointer for merging. return GetArgRCIdentityRoot(Inst) == Arg; default: // Anything that can autorelease interrupts // retainAutoreleaseReturnValue formation. return CanInterruptRV(Class); } } case RetainRVDep: return CanInterruptRV(GetBasicARCInstKind(Inst)); } llvm_unreachable("Invalid dependence flavor"); } /// Walk up the CFG from StartPos (which is in StartBB) and find local and /// non-local dependencies on Arg. /// /// TODO: Cache results? void llvm::objcarc::FindDependencies(DependenceKind Flavor, const Value *Arg, BasicBlock *StartBB, Instruction *StartInst, SmallPtrSetImpl &DependingInsts, SmallPtrSetImpl &Visited, ProvenanceAnalysis &PA) { BasicBlock::iterator StartPos = StartInst; SmallVector, 4> Worklist; Worklist.push_back(std::make_pair(StartBB, StartPos)); do { std::pair Pair = Worklist.pop_back_val(); BasicBlock *LocalStartBB = Pair.first; BasicBlock::iterator LocalStartPos = Pair.second; BasicBlock::iterator StartBBBegin = LocalStartBB->begin(); for (;;) { if (LocalStartPos == StartBBBegin) { pred_iterator PI(LocalStartBB), PE(LocalStartBB, false); if (PI == PE) // If we've reached the function entry, produce a null dependence. DependingInsts.insert(nullptr); else // Add the predecessors to the worklist. do { BasicBlock *PredBB = *PI; if (Visited.insert(PredBB).second) Worklist.push_back(std::make_pair(PredBB, PredBB->end())); } while (++PI != PE); break; } Instruction *Inst = --LocalStartPos; if (Depends(Flavor, Inst, Arg, PA)) { DependingInsts.insert(Inst); break; } } } while (!Worklist.empty()); // Determine whether the original StartBB post-dominates all of the blocks we // visited. If not, insert a sentinal indicating that most optimizations are // not safe. for (const BasicBlock *BB : Visited) { if (BB == StartBB) continue; const TerminatorInst *TI = cast(&BB->back()); for (succ_const_iterator SI(TI), SE(TI, false); SI != SE; ++SI) { const BasicBlock *Succ = *SI; if (Succ != StartBB && !Visited.count(Succ)) { DependingInsts.insert(reinterpret_cast(-1)); return; } } } }