forked from OSchip/llvm-project
Refactor ObjCARCAliasAnalysis into its own file.
llvm-svn: 173662
This commit is contained in:
parent
892711b0db
commit
294e7daaac
|
|
@ -3,6 +3,7 @@ add_llvm_library(LLVMObjCARCOpts
|
||||||
ObjCARCOpts.cpp
|
ObjCARCOpts.cpp
|
||||||
ObjCARCExpand.cpp
|
ObjCARCExpand.cpp
|
||||||
ObjCARCAPElim.cpp
|
ObjCARCAPElim.cpp
|
||||||
|
ObjCARCAliasAnalysis.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
add_dependencies(LLVMObjCARCOpts intrinsics_gen)
|
add_dependencies(LLVMObjCARCOpts intrinsics_gen)
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@
|
||||||
#include "llvm/ADT/StringSwitch.h"
|
#include "llvm/ADT/StringSwitch.h"
|
||||||
#include "llvm/Analysis/AliasAnalysis.h"
|
#include "llvm/Analysis/AliasAnalysis.h"
|
||||||
#include "llvm/Analysis/Passes.h"
|
#include "llvm/Analysis/Passes.h"
|
||||||
|
#include "llvm/Analysis/ValueTracking.h"
|
||||||
#include "llvm/IR/Module.h"
|
#include "llvm/IR/Module.h"
|
||||||
#include "llvm/Pass.h"
|
#include "llvm/Pass.h"
|
||||||
#include "llvm/Support/Debug.h"
|
#include "llvm/Support/Debug.h"
|
||||||
|
|
@ -145,10 +146,81 @@ static raw_ostream &operator<<(raw_ostream &OS, const InstructionClass Class) {
|
||||||
llvm_unreachable("Unknown instruction class!");
|
llvm_unreachable("Unknown instruction class!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// \brief Test if the given class is objc_retain or equivalent.
|
||||||
|
static inline bool IsRetain(InstructionClass Class) {
|
||||||
|
return Class == IC_Retain ||
|
||||||
|
Class == IC_RetainRV;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// \brief Test if the given class is objc_autorelease or equivalent.
|
||||||
|
static inline bool IsAutorelease(InstructionClass Class) {
|
||||||
|
return Class == IC_Autorelease ||
|
||||||
|
Class == IC_AutoreleaseRV;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// \brief Test if the given class represents instructions which return their
|
||||||
|
/// argument verbatim.
|
||||||
|
static inline bool IsForwarding(InstructionClass Class) {
|
||||||
|
// objc_retainBlock technically doesn't always return its argument
|
||||||
|
// verbatim, but it doesn't matter for our purposes here.
|
||||||
|
return Class == IC_Retain ||
|
||||||
|
Class == IC_RetainRV ||
|
||||||
|
Class == IC_Autorelease ||
|
||||||
|
Class == IC_AutoreleaseRV ||
|
||||||
|
Class == IC_RetainBlock ||
|
||||||
|
Class == IC_NoopCast;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// \brief Test if the given class represents instructions which do nothing if
|
||||||
|
/// passed a null pointer.
|
||||||
|
static inline bool IsNoopOnNull(InstructionClass Class) {
|
||||||
|
return Class == IC_Retain ||
|
||||||
|
Class == IC_RetainRV ||
|
||||||
|
Class == IC_Release ||
|
||||||
|
Class == IC_Autorelease ||
|
||||||
|
Class == IC_AutoreleaseRV ||
|
||||||
|
Class == IC_RetainBlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// \brief Test if the given class represents instructions which are always safe
|
||||||
|
/// to mark with the "tail" keyword.
|
||||||
|
static inline bool IsAlwaysTail(InstructionClass Class) {
|
||||||
|
// IC_RetainBlock may be given a stack argument.
|
||||||
|
return Class == IC_Retain ||
|
||||||
|
Class == IC_RetainRV ||
|
||||||
|
Class == IC_AutoreleaseRV;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// \brief Test if the given class represents instructions which are never safe
|
||||||
|
/// to mark with the "tail" keyword.
|
||||||
|
static inline 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 inline bool IsNoThrow(InstructionClass Class) {
|
||||||
|
// objc_retainBlock is not nounwind because it calls user copy constructors
|
||||||
|
// which could theoretically throw.
|
||||||
|
return Class == IC_Retain ||
|
||||||
|
Class == IC_RetainRV ||
|
||||||
|
Class == IC_Release ||
|
||||||
|
Class == IC_Autorelease ||
|
||||||
|
Class == IC_AutoreleaseRV ||
|
||||||
|
Class == IC_AutoreleasepoolPush ||
|
||||||
|
Class == IC_AutoreleasepoolPop;
|
||||||
|
}
|
||||||
|
|
||||||
/// \brief Determine if F is one of the special known Functions. If it isn't,
|
/// \brief Determine if F is one of the special known Functions. If it isn't,
|
||||||
/// return IC_CallOrUser.
|
/// return IC_CallOrUser.
|
||||||
static inline InstructionClass GetFunctionClass(const Function *F) {
|
static InstructionClass GetFunctionClass(const Function *F)
|
||||||
|
LLVM_ATTRIBUTE_USED;
|
||||||
|
static InstructionClass GetFunctionClass(const Function *F) {
|
||||||
Function::const_arg_iterator AI = F->arg_begin(), AE = F->arg_end();
|
Function::const_arg_iterator AI = F->arg_begin(), AE = F->arg_end();
|
||||||
|
|
||||||
// No arguments.
|
// No arguments.
|
||||||
|
|
@ -236,6 +308,48 @@ static inline InstructionClass GetBasicInstructionClass(const Value *V) {
|
||||||
return isa<InvokeInst>(V) ? IC_CallOrUser : IC_User;
|
return isa<InvokeInst>(V) ? IC_CallOrUser : IC_User;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// \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 inline const Value *GetUnderlyingObjCPtr(const Value *V) {
|
||||||
|
for (;;) {
|
||||||
|
V = GetUnderlyingObject(V);
|
||||||
|
if (!IsForwarding(GetBasicInstructionClass(V)))
|
||||||
|
break;
|
||||||
|
V = cast<CallInst>(V)->getArgOperand(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return V;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// \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 inline const Value *StripPointerCastsAndObjCCalls(const Value *V) {
|
||||||
|
for (;;) {
|
||||||
|
V = V->stripPointerCasts();
|
||||||
|
if (!IsForwarding(GetBasicInstructionClass(V)))
|
||||||
|
break;
|
||||||
|
V = cast<CallInst>(V)->getArgOperand(0);
|
||||||
|
}
|
||||||
|
return V;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// \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 inline Value *StripPointerCastsAndObjCCalls(Value *V) {
|
||||||
|
for (;;) {
|
||||||
|
V = V->stripPointerCasts();
|
||||||
|
if (!IsForwarding(GetBasicInstructionClass(V)))
|
||||||
|
break;
|
||||||
|
V = cast<CallInst>(V)->getArgOperand(0);
|
||||||
|
}
|
||||||
|
return V;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
} // end namespace objcarc
|
} // end namespace objcarc
|
||||||
} // end namespace llvm
|
} // end namespace llvm
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,164 @@
|
||||||
|
//===- ObjCARCAliasAnalysis.cpp - ObjC ARC Optimization -*- mode: c++ -*---===//
|
||||||
|
//
|
||||||
|
// 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 a simple ARC-aware AliasAnalysis using special knowledge
|
||||||
|
/// of Objective C to enhance other optimization passes which rely on the Alias
|
||||||
|
/// Analysis infrastructure.
|
||||||
|
///
|
||||||
|
/// 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-aa"
|
||||||
|
#include "ObjCARC.h"
|
||||||
|
#include "ObjCARCAliasAnalysis.h"
|
||||||
|
|
||||||
|
#include "llvm/IR/Instruction.h"
|
||||||
|
#include "llvm/InitializePasses.h"
|
||||||
|
#include "llvm/PassAnalysisSupport.h"
|
||||||
|
#include "llvm/PassSupport.h"
|
||||||
|
|
||||||
|
namespace llvm {
|
||||||
|
class Function;
|
||||||
|
class Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
using namespace llvm;
|
||||||
|
using namespace llvm::objcarc;
|
||||||
|
|
||||||
|
// Register this pass...
|
||||||
|
char ObjCARCAliasAnalysis::ID = 0;
|
||||||
|
INITIALIZE_AG_PASS(ObjCARCAliasAnalysis, AliasAnalysis, "objc-arc-aa",
|
||||||
|
"ObjC-ARC-Based Alias Analysis", false, true, false)
|
||||||
|
|
||||||
|
ImmutablePass *llvm::createObjCARCAliasAnalysisPass() {
|
||||||
|
return new ObjCARCAliasAnalysis();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ObjCARCAliasAnalysis::getAnalysisUsage(AnalysisUsage &AU) const {
|
||||||
|
AU.setPreservesAll();
|
||||||
|
AliasAnalysis::getAnalysisUsage(AU);
|
||||||
|
}
|
||||||
|
|
||||||
|
AliasAnalysis::AliasResult
|
||||||
|
ObjCARCAliasAnalysis::alias(const Location &LocA, const Location &LocB) {
|
||||||
|
if (!EnableARCOpts)
|
||||||
|
return AliasAnalysis::alias(LocA, LocB);
|
||||||
|
|
||||||
|
// First, strip off no-ops, including ObjC-specific no-ops, and try making a
|
||||||
|
// precise alias query.
|
||||||
|
const Value *SA = StripPointerCastsAndObjCCalls(LocA.Ptr);
|
||||||
|
const Value *SB = StripPointerCastsAndObjCCalls(LocB.Ptr);
|
||||||
|
AliasResult Result =
|
||||||
|
AliasAnalysis::alias(Location(SA, LocA.Size, LocA.TBAATag),
|
||||||
|
Location(SB, LocB.Size, LocB.TBAATag));
|
||||||
|
if (Result != MayAlias)
|
||||||
|
return Result;
|
||||||
|
|
||||||
|
// If that failed, climb to the underlying object, including climbing through
|
||||||
|
// ObjC-specific no-ops, and try making an imprecise alias query.
|
||||||
|
const Value *UA = GetUnderlyingObjCPtr(SA);
|
||||||
|
const Value *UB = GetUnderlyingObjCPtr(SB);
|
||||||
|
if (UA != SA || UB != SB) {
|
||||||
|
Result = AliasAnalysis::alias(Location(UA), Location(UB));
|
||||||
|
// We can't use MustAlias or PartialAlias results here because
|
||||||
|
// GetUnderlyingObjCPtr may return an offsetted pointer value.
|
||||||
|
if (Result == NoAlias)
|
||||||
|
return NoAlias;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If that failed, fail. We don't need to chain here, since that's covered
|
||||||
|
// by the earlier precise query.
|
||||||
|
return MayAlias;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
ObjCARCAliasAnalysis::pointsToConstantMemory(const Location &Loc,
|
||||||
|
bool OrLocal) {
|
||||||
|
if (!EnableARCOpts)
|
||||||
|
return AliasAnalysis::pointsToConstantMemory(Loc, OrLocal);
|
||||||
|
|
||||||
|
// First, strip off no-ops, including ObjC-specific no-ops, and try making
|
||||||
|
// a precise alias query.
|
||||||
|
const Value *S = StripPointerCastsAndObjCCalls(Loc.Ptr);
|
||||||
|
if (AliasAnalysis::pointsToConstantMemory(Location(S, Loc.Size, Loc.TBAATag),
|
||||||
|
OrLocal))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// If that failed, climb to the underlying object, including climbing through
|
||||||
|
// ObjC-specific no-ops, and try making an imprecise alias query.
|
||||||
|
const Value *U = GetUnderlyingObjCPtr(S);
|
||||||
|
if (U != S)
|
||||||
|
return AliasAnalysis::pointsToConstantMemory(Location(U), OrLocal);
|
||||||
|
|
||||||
|
// If that failed, fail. We don't need to chain here, since that's covered
|
||||||
|
// by the earlier precise query.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
AliasAnalysis::ModRefBehavior
|
||||||
|
ObjCARCAliasAnalysis::getModRefBehavior(ImmutableCallSite CS) {
|
||||||
|
// We have nothing to do. Just chain to the next AliasAnalysis.
|
||||||
|
return AliasAnalysis::getModRefBehavior(CS);
|
||||||
|
}
|
||||||
|
|
||||||
|
AliasAnalysis::ModRefBehavior
|
||||||
|
ObjCARCAliasAnalysis::getModRefBehavior(const Function *F) {
|
||||||
|
if (!EnableARCOpts)
|
||||||
|
return AliasAnalysis::getModRefBehavior(F);
|
||||||
|
|
||||||
|
switch (GetFunctionClass(F)) {
|
||||||
|
case IC_NoopCast:
|
||||||
|
return DoesNotAccessMemory;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return AliasAnalysis::getModRefBehavior(F);
|
||||||
|
}
|
||||||
|
|
||||||
|
AliasAnalysis::ModRefResult
|
||||||
|
ObjCARCAliasAnalysis::getModRefInfo(ImmutableCallSite CS, const Location &Loc) {
|
||||||
|
if (!EnableARCOpts)
|
||||||
|
return AliasAnalysis::getModRefInfo(CS, Loc);
|
||||||
|
|
||||||
|
switch (GetBasicInstructionClass(CS.getInstruction())) {
|
||||||
|
case IC_Retain:
|
||||||
|
case IC_RetainRV:
|
||||||
|
case IC_Autorelease:
|
||||||
|
case IC_AutoreleaseRV:
|
||||||
|
case IC_NoopCast:
|
||||||
|
case IC_AutoreleasepoolPush:
|
||||||
|
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, because it updates
|
||||||
|
// pointers when it copies block data.
|
||||||
|
return NoModRef;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return AliasAnalysis::getModRefInfo(CS, Loc);
|
||||||
|
}
|
||||||
|
|
||||||
|
AliasAnalysis::ModRefResult
|
||||||
|
ObjCARCAliasAnalysis::getModRefInfo(ImmutableCallSite CS1,
|
||||||
|
ImmutableCallSite CS2) {
|
||||||
|
// TODO: Theoretically we could check for dependencies between objc_* calls
|
||||||
|
// and OnlyAccessesArgumentPointees calls or other well-behaved calls.
|
||||||
|
return AliasAnalysis::getModRefInfo(CS1, CS2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,71 @@
|
||||||
|
//===- ObjCARCAliasAnalysis.h - ObjC ARC Optimization -*- mode: c++ -*-----===//
|
||||||
|
//
|
||||||
|
// The LLVM Compiler Infrastructure
|
||||||
|
//
|
||||||
|
// This file is distributed under the University of Illinois Open Source
|
||||||
|
// License. See LICENSE.TXT for details.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
/// \file
|
||||||
|
/// This file declares a simple ARC-aware AliasAnalysis using special knowledge
|
||||||
|
/// of Objective C to enhance other optimization passes which rely on the Alias
|
||||||
|
/// Analysis infrastructure.
|
||||||
|
///
|
||||||
|
/// 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.
|
||||||
|
///
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#ifndef LLVM_TRANSFORMS_OBJCARC_OBJCARCALIASANALYSIS_H
|
||||||
|
#define LLVM_TRANSFORMS_OBJCARC_OBJCARCALIASANALYSIS_H
|
||||||
|
|
||||||
|
namespace llvm {
|
||||||
|
namespace objcarc {
|
||||||
|
|
||||||
|
/// \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
|
||||||
|
/// even though their offsets are dynamic.
|
||||||
|
class ObjCARCAliasAnalysis : public ImmutablePass,
|
||||||
|
public AliasAnalysis {
|
||||||
|
public:
|
||||||
|
static char ID; // Class identification, replacement for typeinfo
|
||||||
|
ObjCARCAliasAnalysis() : ImmutablePass(ID) {
|
||||||
|
initializeObjCARCAliasAnalysisPass(*PassRegistry::getPassRegistry());
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
virtual void initializePass() {
|
||||||
|
InitializeAliasAnalysis(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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 this;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void getAnalysisUsage(AnalysisUsage &AU) const;
|
||||||
|
virtual AliasResult alias(const Location &LocA, const Location &LocB);
|
||||||
|
virtual bool pointsToConstantMemory(const Location &Loc, bool OrLocal);
|
||||||
|
virtual ModRefBehavior getModRefBehavior(ImmutableCallSite CS);
|
||||||
|
virtual ModRefBehavior getModRefBehavior(const Function *F);
|
||||||
|
virtual ModRefResult getModRefInfo(ImmutableCallSite CS,
|
||||||
|
const Location &Loc);
|
||||||
|
virtual ModRefResult getModRefInfo(ImmutableCallSite CS1,
|
||||||
|
ImmutableCallSite CS2);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace objcarc
|
||||||
|
} // namespace llvm
|
||||||
|
|
||||||
|
#endif // LLVM_TRANSFORMS_OBJCARC_OBJCARCALIASANALYSIS_H
|
||||||
|
|
@ -30,6 +30,7 @@
|
||||||
|
|
||||||
#define DEBUG_TYPE "objc-arc-opts"
|
#define DEBUG_TYPE "objc-arc-opts"
|
||||||
#include "ObjCARC.h"
|
#include "ObjCARC.h"
|
||||||
|
#include "ObjCARCAliasAnalysis.h"
|
||||||
|
|
||||||
#include "llvm/ADT/DenseMap.h"
|
#include "llvm/ADT/DenseMap.h"
|
||||||
#include "llvm/ADT/SmallPtrSet.h"
|
#include "llvm/ADT/SmallPtrSet.h"
|
||||||
|
|
@ -131,7 +132,6 @@ namespace {
|
||||||
/// \defgroup ARCUtilities Utility declarations/definitions specific to ARC.
|
/// \defgroup ARCUtilities Utility declarations/definitions specific to ARC.
|
||||||
/// @{
|
/// @{
|
||||||
|
|
||||||
#include "llvm/Analysis/ValueTracking.h"
|
|
||||||
#include "llvm/IR/Intrinsics.h"
|
#include "llvm/IR/Intrinsics.h"
|
||||||
#include "llvm/Support/CallSite.h"
|
#include "llvm/Support/CallSite.h"
|
||||||
#include "llvm/Transforms/Utils/Local.h"
|
#include "llvm/Transforms/Utils/Local.h"
|
||||||
|
|
@ -260,76 +260,6 @@ static InstructionClass GetInstructionClass(const Value *V) {
|
||||||
return IC_None;
|
return IC_None;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// \brief Test if the given class is objc_retain or equivalent.
|
|
||||||
static bool IsRetain(InstructionClass Class) {
|
|
||||||
return Class == IC_Retain ||
|
|
||||||
Class == IC_RetainRV;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// \brief Test if the given class is objc_autorelease or equivalent.
|
|
||||||
static bool IsAutorelease(InstructionClass Class) {
|
|
||||||
return Class == IC_Autorelease ||
|
|
||||||
Class == IC_AutoreleaseRV;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// \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.
|
|
||||||
return Class == IC_Retain ||
|
|
||||||
Class == IC_RetainRV ||
|
|
||||||
Class == IC_Autorelease ||
|
|
||||||
Class == IC_AutoreleaseRV ||
|
|
||||||
Class == IC_RetainBlock ||
|
|
||||||
Class == IC_NoopCast;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// \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_Release ||
|
|
||||||
Class == IC_Autorelease ||
|
|
||||||
Class == IC_AutoreleaseRV ||
|
|
||||||
Class == IC_RetainBlock;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// \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_AutoreleaseRV;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// \brief Test if the given class represents instructions which are never safe
|
|
||||||
/// to mark with the "tail" keyword.
|
|
||||||
static bool IsNeverTail(InstructionClass Class) {
|
|
||||||
/// It is never safe to tail call objc_autorelease since by tail calling
|
|
||||||
/// objc_autorelease, we also tail call -[NSObject autorelease] which supports
|
|
||||||
/// fast autoreleasing causing our object to be potentially reclaimed from the
|
|
||||||
/// autorelease pool which violates the semantics of __autoreleasing types in
|
|
||||||
/// ARC.
|
|
||||||
return Class == IC_Autorelease;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// \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.
|
|
||||||
return Class == IC_Retain ||
|
|
||||||
Class == IC_RetainRV ||
|
|
||||||
Class == IC_Release ||
|
|
||||||
Class == IC_Autorelease ||
|
|
||||||
Class == IC_AutoreleaseRV ||
|
|
||||||
Class == IC_AutoreleasepoolPush ||
|
|
||||||
Class == IC_AutoreleasepoolPop;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// \brief Erase the given instruction.
|
/// \brief Erase the given instruction.
|
||||||
///
|
///
|
||||||
/// Many ObjC calls return their argument verbatim,
|
/// Many ObjC calls return their argument verbatim,
|
||||||
|
|
@ -354,46 +284,6 @@ static void EraseInstruction(Instruction *CI) {
|
||||||
RecursivelyDeleteTriviallyDeadInstructions(OldArg);
|
RecursivelyDeleteTriviallyDeadInstructions(OldArg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// \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);
|
|
||||||
if (!IsForwarding(GetBasicInstructionClass(V)))
|
|
||||||
break;
|
|
||||||
V = cast<CallInst>(V)->getArgOperand(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
return V;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// \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();
|
|
||||||
if (!IsForwarding(GetBasicInstructionClass(V)))
|
|
||||||
break;
|
|
||||||
V = cast<CallInst>(V)->getArgOperand(0);
|
|
||||||
}
|
|
||||||
return V;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// \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();
|
|
||||||
if (!IsForwarding(GetBasicInstructionClass(V)))
|
|
||||||
break;
|
|
||||||
V = cast<CallInst>(V)->getArgOperand(0);
|
|
||||||
}
|
|
||||||
return V;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// \brief Assuming the given instruction is one of the special calls such as
|
/// \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
|
/// objc_retain or objc_release, return the argument value, stripped of no-op
|
||||||
/// casts and forwarding calls.
|
/// casts and forwarding calls.
|
||||||
|
|
@ -551,177 +441,6 @@ static bool DoesObjCBlockEscape(const Value *BlockPtr) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @}
|
|
||||||
///
|
|
||||||
/// \defgroup ARCAA Extends alias analysis using ObjC specific knowledge.
|
|
||||||
/// @{
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
/// \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
|
|
||||||
/// even though their offsets are dynamic.
|
|
||||||
class ObjCARCAliasAnalysis : public ImmutablePass,
|
|
||||||
public AliasAnalysis {
|
|
||||||
public:
|
|
||||||
static char ID; // Class identification, replacement for typeinfo
|
|
||||||
ObjCARCAliasAnalysis() : ImmutablePass(ID) {
|
|
||||||
initializeObjCARCAliasAnalysisPass(*PassRegistry::getPassRegistry());
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
virtual void initializePass() {
|
|
||||||
InitializeAliasAnalysis(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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 this;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void getAnalysisUsage(AnalysisUsage &AU) const;
|
|
||||||
virtual AliasResult alias(const Location &LocA, const Location &LocB);
|
|
||||||
virtual bool pointsToConstantMemory(const Location &Loc, bool OrLocal);
|
|
||||||
virtual ModRefBehavior getModRefBehavior(ImmutableCallSite CS);
|
|
||||||
virtual ModRefBehavior getModRefBehavior(const Function *F);
|
|
||||||
virtual ModRefResult getModRefInfo(ImmutableCallSite CS,
|
|
||||||
const Location &Loc);
|
|
||||||
virtual ModRefResult getModRefInfo(ImmutableCallSite CS1,
|
|
||||||
ImmutableCallSite CS2);
|
|
||||||
};
|
|
||||||
} // End of anonymous namespace
|
|
||||||
|
|
||||||
// Register this pass...
|
|
||||||
char ObjCARCAliasAnalysis::ID = 0;
|
|
||||||
INITIALIZE_AG_PASS(ObjCARCAliasAnalysis, AliasAnalysis, "objc-arc-aa",
|
|
||||||
"ObjC-ARC-Based Alias Analysis", false, true, false)
|
|
||||||
|
|
||||||
ImmutablePass *llvm::createObjCARCAliasAnalysisPass() {
|
|
||||||
return new ObjCARCAliasAnalysis();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
ObjCARCAliasAnalysis::getAnalysisUsage(AnalysisUsage &AU) const {
|
|
||||||
AU.setPreservesAll();
|
|
||||||
AliasAnalysis::getAnalysisUsage(AU);
|
|
||||||
}
|
|
||||||
|
|
||||||
AliasAnalysis::AliasResult
|
|
||||||
ObjCARCAliasAnalysis::alias(const Location &LocA, const Location &LocB) {
|
|
||||||
if (!EnableARCOpts)
|
|
||||||
return AliasAnalysis::alias(LocA, LocB);
|
|
||||||
|
|
||||||
// First, strip off no-ops, including ObjC-specific no-ops, and try making a
|
|
||||||
// precise alias query.
|
|
||||||
const Value *SA = StripPointerCastsAndObjCCalls(LocA.Ptr);
|
|
||||||
const Value *SB = StripPointerCastsAndObjCCalls(LocB.Ptr);
|
|
||||||
AliasResult Result =
|
|
||||||
AliasAnalysis::alias(Location(SA, LocA.Size, LocA.TBAATag),
|
|
||||||
Location(SB, LocB.Size, LocB.TBAATag));
|
|
||||||
if (Result != MayAlias)
|
|
||||||
return Result;
|
|
||||||
|
|
||||||
// If that failed, climb to the underlying object, including climbing through
|
|
||||||
// ObjC-specific no-ops, and try making an imprecise alias query.
|
|
||||||
const Value *UA = GetUnderlyingObjCPtr(SA);
|
|
||||||
const Value *UB = GetUnderlyingObjCPtr(SB);
|
|
||||||
if (UA != SA || UB != SB) {
|
|
||||||
Result = AliasAnalysis::alias(Location(UA), Location(UB));
|
|
||||||
// We can't use MustAlias or PartialAlias results here because
|
|
||||||
// GetUnderlyingObjCPtr may return an offsetted pointer value.
|
|
||||||
if (Result == NoAlias)
|
|
||||||
return NoAlias;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If that failed, fail. We don't need to chain here, since that's covered
|
|
||||||
// by the earlier precise query.
|
|
||||||
return MayAlias;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
ObjCARCAliasAnalysis::pointsToConstantMemory(const Location &Loc,
|
|
||||||
bool OrLocal) {
|
|
||||||
if (!EnableARCOpts)
|
|
||||||
return AliasAnalysis::pointsToConstantMemory(Loc, OrLocal);
|
|
||||||
|
|
||||||
// First, strip off no-ops, including ObjC-specific no-ops, and try making
|
|
||||||
// a precise alias query.
|
|
||||||
const Value *S = StripPointerCastsAndObjCCalls(Loc.Ptr);
|
|
||||||
if (AliasAnalysis::pointsToConstantMemory(Location(S, Loc.Size, Loc.TBAATag),
|
|
||||||
OrLocal))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// If that failed, climb to the underlying object, including climbing through
|
|
||||||
// ObjC-specific no-ops, and try making an imprecise alias query.
|
|
||||||
const Value *U = GetUnderlyingObjCPtr(S);
|
|
||||||
if (U != S)
|
|
||||||
return AliasAnalysis::pointsToConstantMemory(Location(U), OrLocal);
|
|
||||||
|
|
||||||
// If that failed, fail. We don't need to chain here, since that's covered
|
|
||||||
// by the earlier precise query.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
AliasAnalysis::ModRefBehavior
|
|
||||||
ObjCARCAliasAnalysis::getModRefBehavior(ImmutableCallSite CS) {
|
|
||||||
// We have nothing to do. Just chain to the next AliasAnalysis.
|
|
||||||
return AliasAnalysis::getModRefBehavior(CS);
|
|
||||||
}
|
|
||||||
|
|
||||||
AliasAnalysis::ModRefBehavior
|
|
||||||
ObjCARCAliasAnalysis::getModRefBehavior(const Function *F) {
|
|
||||||
if (!EnableARCOpts)
|
|
||||||
return AliasAnalysis::getModRefBehavior(F);
|
|
||||||
|
|
||||||
switch (GetFunctionClass(F)) {
|
|
||||||
case IC_NoopCast:
|
|
||||||
return DoesNotAccessMemory;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return AliasAnalysis::getModRefBehavior(F);
|
|
||||||
}
|
|
||||||
|
|
||||||
AliasAnalysis::ModRefResult
|
|
||||||
ObjCARCAliasAnalysis::getModRefInfo(ImmutableCallSite CS, const Location &Loc) {
|
|
||||||
if (!EnableARCOpts)
|
|
||||||
return AliasAnalysis::getModRefInfo(CS, Loc);
|
|
||||||
|
|
||||||
switch (GetBasicInstructionClass(CS.getInstruction())) {
|
|
||||||
case IC_Retain:
|
|
||||||
case IC_RetainRV:
|
|
||||||
case IC_Autorelease:
|
|
||||||
case IC_AutoreleaseRV:
|
|
||||||
case IC_NoopCast:
|
|
||||||
case IC_AutoreleasepoolPush:
|
|
||||||
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, because it updates
|
|
||||||
// pointers when it copies block data.
|
|
||||||
return NoModRef;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return AliasAnalysis::getModRefInfo(CS, Loc);
|
|
||||||
}
|
|
||||||
|
|
||||||
AliasAnalysis::ModRefResult
|
|
||||||
ObjCARCAliasAnalysis::getModRefInfo(ImmutableCallSite CS1,
|
|
||||||
ImmutableCallSite CS2) {
|
|
||||||
// TODO: Theoretically we could check for dependencies between objc_* calls
|
|
||||||
// and OnlyAccessesArgumentPointees calls or other well-behaved calls.
|
|
||||||
return AliasAnalysis::getModRefInfo(CS1, CS2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @}
|
/// @}
|
||||||
///
|
///
|
||||||
/// \defgroup ARCOpt ARC Optimization.
|
/// \defgroup ARCOpt ARC Optimization.
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue