forked from OSchip/llvm-project
[objc-arc] Change the InstructionClass to be an enum class called ARCInstKind.
I also renamed ObjCARCUtil.cpp -> ARCInstKind.cpp. That file only contained items related to ARCInstKind anyways. llvm-svn: 229905
This commit is contained in:
parent
a747e5935d
commit
6f729fa675
|
@ -0,0 +1,293 @@
|
|||
//===- ARCInstKind.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 several utility functions used by various ARC
|
||||
/// optimizations which are IMHO too big to be in a header file.
|
||||
///
|
||||
/// 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 "llvm/IR/Intrinsics.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::objcarc;
|
||||
|
||||
raw_ostream &llvm::objcarc::operator<<(raw_ostream &OS,
|
||||
const ARCInstKind Class) {
|
||||
switch (Class) {
|
||||
case ARCInstKind::Retain:
|
||||
return OS << "ARCInstKind::Retain";
|
||||
case ARCInstKind::RetainRV:
|
||||
return OS << "ARCInstKind::RetainRV";
|
||||
case ARCInstKind::RetainBlock:
|
||||
return OS << "ARCInstKind::RetainBlock";
|
||||
case ARCInstKind::Release:
|
||||
return OS << "ARCInstKind::Release";
|
||||
case ARCInstKind::Autorelease:
|
||||
return OS << "ARCInstKind::Autorelease";
|
||||
case ARCInstKind::AutoreleaseRV:
|
||||
return OS << "ARCInstKind::AutoreleaseRV";
|
||||
case ARCInstKind::AutoreleasepoolPush:
|
||||
return OS << "ARCInstKind::AutoreleasepoolPush";
|
||||
case ARCInstKind::AutoreleasepoolPop:
|
||||
return OS << "ARCInstKind::AutoreleasepoolPop";
|
||||
case ARCInstKind::NoopCast:
|
||||
return OS << "ARCInstKind::NoopCast";
|
||||
case ARCInstKind::FusedRetainAutorelease:
|
||||
return OS << "ARCInstKind::FusedRetainAutorelease";
|
||||
case ARCInstKind::FusedRetainAutoreleaseRV:
|
||||
return OS << "ARCInstKind::FusedRetainAutoreleaseRV";
|
||||
case ARCInstKind::LoadWeakRetained:
|
||||
return OS << "ARCInstKind::LoadWeakRetained";
|
||||
case ARCInstKind::StoreWeak:
|
||||
return OS << "ARCInstKind::StoreWeak";
|
||||
case ARCInstKind::InitWeak:
|
||||
return OS << "ARCInstKind::InitWeak";
|
||||
case ARCInstKind::LoadWeak:
|
||||
return OS << "ARCInstKind::LoadWeak";
|
||||
case ARCInstKind::MoveWeak:
|
||||
return OS << "ARCInstKind::MoveWeak";
|
||||
case ARCInstKind::CopyWeak:
|
||||
return OS << "ARCInstKind::CopyWeak";
|
||||
case ARCInstKind::DestroyWeak:
|
||||
return OS << "ARCInstKind::DestroyWeak";
|
||||
case ARCInstKind::StoreStrong:
|
||||
return OS << "ARCInstKind::StoreStrong";
|
||||
case ARCInstKind::CallOrUser:
|
||||
return OS << "ARCInstKind::CallOrUser";
|
||||
case ARCInstKind::Call:
|
||||
return OS << "ARCInstKind::Call";
|
||||
case ARCInstKind::User:
|
||||
return OS << "ARCInstKind::User";
|
||||
case ARCInstKind::IntrinsicUser:
|
||||
return OS << "ARCInstKind::IntrinsicUser";
|
||||
case ARCInstKind::None:
|
||||
return OS << "ARCInstKind::None";
|
||||
}
|
||||
llvm_unreachable("Unknown instruction class!");
|
||||
}
|
||||
|
||||
ARCInstKind llvm::objcarc::GetFunctionClass(const Function *F) {
|
||||
Function::const_arg_iterator AI = F->arg_begin(), AE = F->arg_end();
|
||||
|
||||
// No (mandatory) arguments.
|
||||
if (AI == AE)
|
||||
return StringSwitch<ARCInstKind>(F->getName())
|
||||
.Case("objc_autoreleasePoolPush", ARCInstKind::AutoreleasepoolPush)
|
||||
.Case("clang.arc.use", ARCInstKind::IntrinsicUser)
|
||||
.Default(ARCInstKind::CallOrUser);
|
||||
|
||||
// One argument.
|
||||
const Argument *A0 = AI++;
|
||||
if (AI == AE)
|
||||
// Argument is a pointer.
|
||||
if (PointerType *PTy = dyn_cast<PointerType>(A0->getType())) {
|
||||
Type *ETy = PTy->getElementType();
|
||||
// Argument is i8*.
|
||||
if (ETy->isIntegerTy(8))
|
||||
return StringSwitch<ARCInstKind>(F->getName())
|
||||
.Case("objc_retain", ARCInstKind::Retain)
|
||||
.Case("objc_retainAutoreleasedReturnValue", ARCInstKind::RetainRV)
|
||||
.Case("objc_retainBlock", ARCInstKind::RetainBlock)
|
||||
.Case("objc_release", ARCInstKind::Release)
|
||||
.Case("objc_autorelease", ARCInstKind::Autorelease)
|
||||
.Case("objc_autoreleaseReturnValue", ARCInstKind::AutoreleaseRV)
|
||||
.Case("objc_autoreleasePoolPop", ARCInstKind::AutoreleasepoolPop)
|
||||
.Case("objc_retainedObject", ARCInstKind::NoopCast)
|
||||
.Case("objc_unretainedObject", ARCInstKind::NoopCast)
|
||||
.Case("objc_unretainedPointer", ARCInstKind::NoopCast)
|
||||
.Case("objc_retain_autorelease",
|
||||
ARCInstKind::FusedRetainAutorelease)
|
||||
.Case("objc_retainAutorelease", ARCInstKind::FusedRetainAutorelease)
|
||||
.Case("objc_retainAutoreleaseReturnValue",
|
||||
ARCInstKind::FusedRetainAutoreleaseRV)
|
||||
.Case("objc_sync_enter", ARCInstKind::User)
|
||||
.Case("objc_sync_exit", ARCInstKind::User)
|
||||
.Default(ARCInstKind::CallOrUser);
|
||||
|
||||
// Argument is i8**
|
||||
if (PointerType *Pte = dyn_cast<PointerType>(ETy))
|
||||
if (Pte->getElementType()->isIntegerTy(8))
|
||||
return StringSwitch<ARCInstKind>(F->getName())
|
||||
.Case("objc_loadWeakRetained", ARCInstKind::LoadWeakRetained)
|
||||
.Case("objc_loadWeak", ARCInstKind::LoadWeak)
|
||||
.Case("objc_destroyWeak", ARCInstKind::DestroyWeak)
|
||||
.Default(ARCInstKind::CallOrUser);
|
||||
}
|
||||
|
||||
// Two arguments, first is i8**.
|
||||
const Argument *A1 = AI++;
|
||||
if (AI == AE)
|
||||
if (PointerType *PTy = dyn_cast<PointerType>(A0->getType()))
|
||||
if (PointerType *Pte = dyn_cast<PointerType>(PTy->getElementType()))
|
||||
if (Pte->getElementType()->isIntegerTy(8))
|
||||
if (PointerType *PTy1 = dyn_cast<PointerType>(A1->getType())) {
|
||||
Type *ETy1 = PTy1->getElementType();
|
||||
// Second argument is i8*
|
||||
if (ETy1->isIntegerTy(8))
|
||||
return StringSwitch<ARCInstKind>(F->getName())
|
||||
.Case("objc_storeWeak", ARCInstKind::StoreWeak)
|
||||
.Case("objc_initWeak", ARCInstKind::InitWeak)
|
||||
.Case("objc_storeStrong", ARCInstKind::StoreStrong)
|
||||
.Default(ARCInstKind::CallOrUser);
|
||||
// Second argument is i8**.
|
||||
if (PointerType *Pte1 = dyn_cast<PointerType>(ETy1))
|
||||
if (Pte1->getElementType()->isIntegerTy(8))
|
||||
return StringSwitch<ARCInstKind>(F->getName())
|
||||
.Case("objc_moveWeak", ARCInstKind::MoveWeak)
|
||||
.Case("objc_copyWeak", ARCInstKind::CopyWeak)
|
||||
// Ignore annotation calls. This is important to stop the
|
||||
// optimizer from treating annotations as uses which would
|
||||
// make the state of the pointers they are attempting to
|
||||
// elucidate to be incorrect.
|
||||
.Case("llvm.arc.annotation.topdown.bbstart",
|
||||
ARCInstKind::None)
|
||||
.Case("llvm.arc.annotation.topdown.bbend",
|
||||
ARCInstKind::None)
|
||||
.Case("llvm.arc.annotation.bottomup.bbstart",
|
||||
ARCInstKind::None)
|
||||
.Case("llvm.arc.annotation.bottomup.bbend",
|
||||
ARCInstKind::None)
|
||||
.Default(ARCInstKind::CallOrUser);
|
||||
}
|
||||
|
||||
// Anything else.
|
||||
return ARCInstKind::CallOrUser;
|
||||
}
|
||||
|
||||
/// \brief Determine what kind of construct V is.
|
||||
ARCInstKind llvm::objcarc::GetARCInstKind(const Value *V) {
|
||||
if (const Instruction *I = dyn_cast<Instruction>(V)) {
|
||||
// Any instruction other than bitcast and gep with a pointer operand have a
|
||||
// use of an objc pointer. Bitcasts, GEPs, Selects, PHIs transfer a pointer
|
||||
// to a subsequent use, rather than using it themselves, in this sense.
|
||||
// As a short cut, several other opcodes are known to have no pointer
|
||||
// operands of interest. And ret is never followed by a release, so it's
|
||||
// not interesting to examine.
|
||||
switch (I->getOpcode()) {
|
||||
case Instruction::Call: {
|
||||
const CallInst *CI = cast<CallInst>(I);
|
||||
// Check for calls to special functions.
|
||||
if (const Function *F = CI->getCalledFunction()) {
|
||||
ARCInstKind Class = GetFunctionClass(F);
|
||||
if (Class != ARCInstKind::CallOrUser)
|
||||
return Class;
|
||||
|
||||
// None of the intrinsic functions do objc_release. For intrinsics, the
|
||||
// only question is whether or not they may be users.
|
||||
switch (F->getIntrinsicID()) {
|
||||
case Intrinsic::returnaddress:
|
||||
case Intrinsic::frameaddress:
|
||||
case Intrinsic::stacksave:
|
||||
case Intrinsic::stackrestore:
|
||||
case Intrinsic::vastart:
|
||||
case Intrinsic::vacopy:
|
||||
case Intrinsic::vaend:
|
||||
case Intrinsic::objectsize:
|
||||
case Intrinsic::prefetch:
|
||||
case Intrinsic::stackprotector:
|
||||
case Intrinsic::eh_return_i32:
|
||||
case Intrinsic::eh_return_i64:
|
||||
case Intrinsic::eh_typeid_for:
|
||||
case Intrinsic::eh_dwarf_cfa:
|
||||
case Intrinsic::eh_sjlj_lsda:
|
||||
case Intrinsic::eh_sjlj_functioncontext:
|
||||
case Intrinsic::init_trampoline:
|
||||
case Intrinsic::adjust_trampoline:
|
||||
case Intrinsic::lifetime_start:
|
||||
case Intrinsic::lifetime_end:
|
||||
case Intrinsic::invariant_start:
|
||||
case Intrinsic::invariant_end:
|
||||
// Don't let dbg info affect our results.
|
||||
case Intrinsic::dbg_declare:
|
||||
case Intrinsic::dbg_value:
|
||||
// Short cut: Some intrinsics obviously don't use ObjC pointers.
|
||||
return ARCInstKind::None;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return GetCallSiteClass(CI);
|
||||
}
|
||||
case Instruction::Invoke:
|
||||
return GetCallSiteClass(cast<InvokeInst>(I));
|
||||
case Instruction::BitCast:
|
||||
case Instruction::GetElementPtr:
|
||||
case Instruction::Select:
|
||||
case Instruction::PHI:
|
||||
case Instruction::Ret:
|
||||
case Instruction::Br:
|
||||
case Instruction::Switch:
|
||||
case Instruction::IndirectBr:
|
||||
case Instruction::Alloca:
|
||||
case Instruction::VAArg:
|
||||
case Instruction::Add:
|
||||
case Instruction::FAdd:
|
||||
case Instruction::Sub:
|
||||
case Instruction::FSub:
|
||||
case Instruction::Mul:
|
||||
case Instruction::FMul:
|
||||
case Instruction::SDiv:
|
||||
case Instruction::UDiv:
|
||||
case Instruction::FDiv:
|
||||
case Instruction::SRem:
|
||||
case Instruction::URem:
|
||||
case Instruction::FRem:
|
||||
case Instruction::Shl:
|
||||
case Instruction::LShr:
|
||||
case Instruction::AShr:
|
||||
case Instruction::And:
|
||||
case Instruction::Or:
|
||||
case Instruction::Xor:
|
||||
case Instruction::SExt:
|
||||
case Instruction::ZExt:
|
||||
case Instruction::Trunc:
|
||||
case Instruction::IntToPtr:
|
||||
case Instruction::FCmp:
|
||||
case Instruction::FPTrunc:
|
||||
case Instruction::FPExt:
|
||||
case Instruction::FPToUI:
|
||||
case Instruction::FPToSI:
|
||||
case Instruction::UIToFP:
|
||||
case Instruction::SIToFP:
|
||||
case Instruction::InsertElement:
|
||||
case Instruction::ExtractElement:
|
||||
case Instruction::ShuffleVector:
|
||||
case Instruction::ExtractValue:
|
||||
break;
|
||||
case Instruction::ICmp:
|
||||
// Comparing a pointer with null, or any other constant, isn't an
|
||||
// interesting use, because we don't care what the pointer points to, or
|
||||
// about the values of any other dynamic reference-counted pointers.
|
||||
if (IsPotentialRetainableObjPtr(I->getOperand(1)))
|
||||
return ARCInstKind::User;
|
||||
break;
|
||||
default:
|
||||
// For anything else, check all the operands.
|
||||
// Note that this includes both operands of a Store: while the first
|
||||
// operand isn't actually being dereferenced, it is being stored to
|
||||
// memory where we can no longer track who might read it and dereference
|
||||
// it, so we have to consider it potentially used.
|
||||
for (User::const_op_iterator OI = I->op_begin(), OE = I->op_end();
|
||||
OI != OE; ++OI)
|
||||
if (IsPotentialRetainableObjPtr(*OI))
|
||||
return ARCInstKind::User;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, it's totally inert for ARC purposes.
|
||||
return ARCInstKind::None;
|
||||
}
|
|
@ -0,0 +1,168 @@
|
|||
//===--- ARCInstKind.h - ARC instruction equivalence classes -*- C++ -*----===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_LIB_TRANSFORMS_OBJCARC_ARCINSTKIND_H
|
||||
#define LLVM_LIB_TRANSFORMS_OBJCARC_ARCINSTKIND_H
|
||||
|
||||
#include "llvm/IR/Instructions.h"
|
||||
#include "llvm/IR/Function.h"
|
||||
|
||||
namespace llvm {
|
||||
namespace objcarc {
|
||||
|
||||
/// \enum ARCInstKind
|
||||
///
|
||||
/// \brief Equivalence classes of instructions in the ARC Model.
|
||||
///
|
||||
/// Since we do not have "instructions" to represent ARC concepts in LLVM IR,
|
||||
/// we instead operate on equivalence classes of instructions.
|
||||
///
|
||||
/// TODO: This should be split into two enums: a runtime entry point enum
|
||||
/// (possibly united with the ARCRuntimeEntrypoint class) and an enum that deals
|
||||
/// with effects of instructions in the ARC model (which would handle the notion
|
||||
/// of a User or CallOrUser).
|
||||
enum class ARCInstKind {
|
||||
Retain, ///< objc_retain
|
||||
RetainRV, ///< objc_retainAutoreleasedReturnValue
|
||||
RetainBlock, ///< objc_retainBlock
|
||||
Release, ///< objc_release
|
||||
Autorelease, ///< objc_autorelease
|
||||
AutoreleaseRV, ///< objc_autoreleaseReturnValue
|
||||
AutoreleasepoolPush, ///< objc_autoreleasePoolPush
|
||||
AutoreleasepoolPop, ///< objc_autoreleasePoolPop
|
||||
NoopCast, ///< objc_retainedObject, etc.
|
||||
FusedRetainAutorelease, ///< objc_retainAutorelease
|
||||
FusedRetainAutoreleaseRV, ///< objc_retainAutoreleaseReturnValue
|
||||
LoadWeakRetained, ///< objc_loadWeakRetained (primitive)
|
||||
StoreWeak, ///< objc_storeWeak (primitive)
|
||||
InitWeak, ///< objc_initWeak (derived)
|
||||
LoadWeak, ///< objc_loadWeak (derived)
|
||||
MoveWeak, ///< objc_moveWeak (derived)
|
||||
CopyWeak, ///< objc_copyWeak (derived)
|
||||
DestroyWeak, ///< objc_destroyWeak (derived)
|
||||
StoreStrong, ///< objc_storeStrong (derived)
|
||||
IntrinsicUser, ///< clang.arc.use
|
||||
CallOrUser, ///< could call objc_release and/or "use" pointers
|
||||
Call, ///< could call objc_release
|
||||
User, ///< could "use" a pointer
|
||||
None ///< anything else
|
||||
};
|
||||
|
||||
raw_ostream &operator<<(raw_ostream &OS, const ARCInstKind Class);
|
||||
|
||||
/// \brief Test if the given class is a kind of user.
|
||||
inline static bool IsUser(ARCInstKind Class) {
|
||||
return Class == ARCInstKind::User || Class == ARCInstKind::CallOrUser ||
|
||||
Class == ARCInstKind::IntrinsicUser;
|
||||
}
|
||||
|
||||
/// \brief Test if the given class is objc_retain or equivalent.
|
||||
static inline bool IsRetain(ARCInstKind Class) {
|
||||
return Class == ARCInstKind::Retain || Class == ARCInstKind::RetainRV;
|
||||
}
|
||||
|
||||
/// \brief Test if the given class is objc_autorelease or equivalent.
|
||||
static inline bool IsAutorelease(ARCInstKind Class) {
|
||||
return Class == ARCInstKind::Autorelease ||
|
||||
Class == ARCInstKind::AutoreleaseRV;
|
||||
}
|
||||
|
||||
/// \brief Test if the given class represents instructions which return their
|
||||
/// argument verbatim.
|
||||
static inline bool IsForwarding(ARCInstKind Class) {
|
||||
return Class == ARCInstKind::Retain || Class == ARCInstKind::RetainRV ||
|
||||
Class == ARCInstKind::Autorelease ||
|
||||
Class == ARCInstKind::AutoreleaseRV || Class == ARCInstKind::NoopCast;
|
||||
}
|
||||
|
||||
/// \brief Test if the given class represents instructions which do nothing if
|
||||
/// passed a null pointer.
|
||||
static inline bool IsNoopOnNull(ARCInstKind Class) {
|
||||
return Class == ARCInstKind::Retain || Class == ARCInstKind::RetainRV ||
|
||||
Class == ARCInstKind::Release || Class == ARCInstKind::Autorelease ||
|
||||
Class == ARCInstKind::AutoreleaseRV ||
|
||||
Class == ARCInstKind::RetainBlock;
|
||||
}
|
||||
|
||||
/// \brief Test if the given class represents instructions which are always safe
|
||||
/// to mark with the "tail" keyword.
|
||||
static inline bool IsAlwaysTail(ARCInstKind Class) {
|
||||
// ARCInstKind::RetainBlock may be given a stack argument.
|
||||
return Class == ARCInstKind::Retain || Class == ARCInstKind::RetainRV ||
|
||||
Class == ARCInstKind::AutoreleaseRV;
|
||||
}
|
||||
|
||||
/// \brief Test if the given class represents instructions which are never safe
|
||||
/// to mark with the "tail" keyword.
|
||||
static inline bool IsNeverTail(ARCInstKind 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 == ARCInstKind::Autorelease;
|
||||
}
|
||||
|
||||
/// \brief Test if the given class represents instructions which are always safe
|
||||
/// to mark with the nounwind attribute.
|
||||
static inline bool IsNoThrow(ARCInstKind Class) {
|
||||
// objc_retainBlock is not nounwind because it calls user copy constructors
|
||||
// which could theoretically throw.
|
||||
return Class == ARCInstKind::Retain || Class == ARCInstKind::RetainRV ||
|
||||
Class == ARCInstKind::Release || Class == ARCInstKind::Autorelease ||
|
||||
Class == ARCInstKind::AutoreleaseRV ||
|
||||
Class == ARCInstKind::AutoreleasepoolPush ||
|
||||
Class == ARCInstKind::AutoreleasepoolPop;
|
||||
}
|
||||
|
||||
/// Test whether the given instruction can autorelease any pointer or cause an
|
||||
/// autoreleasepool pop.
|
||||
static inline bool CanInterruptRV(ARCInstKind Class) {
|
||||
switch (Class) {
|
||||
case ARCInstKind::AutoreleasepoolPop:
|
||||
case ARCInstKind::CallOrUser:
|
||||
case ARCInstKind::Call:
|
||||
case ARCInstKind::Autorelease:
|
||||
case ARCInstKind::AutoreleaseRV:
|
||||
case ARCInstKind::FusedRetainAutorelease:
|
||||
case ARCInstKind::FusedRetainAutoreleaseRV:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// \brief Determine if F is one of the special known Functions. If it isn't,
|
||||
/// return ARCInstKind::CallOrUser.
|
||||
ARCInstKind GetFunctionClass(const Function *F);
|
||||
|
||||
/// \brief Determine which objc runtime call instruction class V belongs to.
|
||||
///
|
||||
/// This is similar to GetARCInstKind except that it only detects objc
|
||||
/// runtime calls. This allows it to be faster.
|
||||
///
|
||||
static inline ARCInstKind GetBasicARCInstKind(const Value *V) {
|
||||
if (const CallInst *CI = dyn_cast<CallInst>(V)) {
|
||||
if (const Function *F = CI->getCalledFunction())
|
||||
return GetFunctionClass(F);
|
||||
// Otherwise, be conservative.
|
||||
return ARCInstKind::CallOrUser;
|
||||
}
|
||||
|
||||
// Otherwise, be conservative.
|
||||
return isa<InvokeInst>(V) ? ARCInstKind::CallOrUser : ARCInstKind::User;
|
||||
}
|
||||
|
||||
/// \brief Determine what kind of construct V is.
|
||||
ARCInstKind GetARCInstKind(const Value *V);
|
||||
|
||||
} // end namespace objcarc
|
||||
} // end namespace llvm
|
||||
|
||||
#endif
|
|
@ -4,7 +4,7 @@ add_llvm_library(LLVMObjCARCOpts
|
|||
ObjCARCExpand.cpp
|
||||
ObjCARCAPElim.cpp
|
||||
ObjCARCAliasAnalysis.cpp
|
||||
ObjCARCUtil.cpp
|
||||
ARCInstKind.cpp
|
||||
ObjCARCContract.cpp
|
||||
DependencyAnalysis.cpp
|
||||
ProvenanceAnalysis.cpp
|
||||
|
|
|
@ -32,15 +32,14 @@ using namespace llvm::objcarc;
|
|||
|
||||
/// 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,
|
||||
bool llvm::objcarc::CanAlterRefCount(const Instruction *Inst, const Value *Ptr,
|
||||
ProvenanceAnalysis &PA,
|
||||
InstructionClass Class) {
|
||||
ARCInstKind Class) {
|
||||
switch (Class) {
|
||||
case IC_Autorelease:
|
||||
case IC_AutoreleaseRV:
|
||||
case IC_IntrinsicUser:
|
||||
case IC_User:
|
||||
case ARCInstKind::Autorelease:
|
||||
case ARCInstKind::AutoreleaseRV:
|
||||
case ARCInstKind::IntrinsicUser:
|
||||
case ARCInstKind::User:
|
||||
// These operations never directly modify a reference count.
|
||||
return false;
|
||||
default: break;
|
||||
|
@ -69,11 +68,11 @@ llvm::objcarc::CanAlterRefCount(const Instruction *Inst, const Value *Ptr,
|
|||
|
||||
/// 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, InstructionClass Class) {
|
||||
// IC_Call operations (as opposed to IC_CallOrUser) never "use" objc pointers.
|
||||
if (Class == IC_Call)
|
||||
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;
|
||||
|
||||
// Consider various instructions which may have pointer arguments which are
|
||||
|
@ -123,11 +122,11 @@ llvm::objcarc::Depends(DependenceKind Flavor, Instruction *Inst,
|
|||
|
||||
switch (Flavor) {
|
||||
case NeedsPositiveRetainCount: {
|
||||
InstructionClass Class = GetInstructionClass(Inst);
|
||||
ARCInstKind Class = GetARCInstKind(Inst);
|
||||
switch (Class) {
|
||||
case IC_AutoreleasepoolPop:
|
||||
case IC_AutoreleasepoolPush:
|
||||
case IC_None:
|
||||
case ARCInstKind::AutoreleasepoolPop:
|
||||
case ARCInstKind::AutoreleasepoolPush:
|
||||
case ARCInstKind::None:
|
||||
return false;
|
||||
default:
|
||||
return CanUse(Inst, Arg, PA, Class);
|
||||
|
@ -135,10 +134,10 @@ llvm::objcarc::Depends(DependenceKind Flavor, Instruction *Inst,
|
|||
}
|
||||
|
||||
case AutoreleasePoolBoundary: {
|
||||
InstructionClass Class = GetInstructionClass(Inst);
|
||||
ARCInstKind Class = GetARCInstKind(Inst);
|
||||
switch (Class) {
|
||||
case IC_AutoreleasepoolPop:
|
||||
case IC_AutoreleasepoolPush:
|
||||
case ARCInstKind::AutoreleasepoolPop:
|
||||
case ARCInstKind::AutoreleasepoolPush:
|
||||
// These mark the end and begin of an autorelease pool scope.
|
||||
return true;
|
||||
default:
|
||||
|
@ -148,13 +147,13 @@ llvm::objcarc::Depends(DependenceKind Flavor, Instruction *Inst,
|
|||
}
|
||||
|
||||
case CanChangeRetainCount: {
|
||||
InstructionClass Class = GetInstructionClass(Inst);
|
||||
ARCInstKind Class = GetARCInstKind(Inst);
|
||||
switch (Class) {
|
||||
case IC_AutoreleasepoolPop:
|
||||
case ARCInstKind::AutoreleasepoolPop:
|
||||
// Conservatively assume this can decrement any count.
|
||||
return true;
|
||||
case IC_AutoreleasepoolPush:
|
||||
case IC_None:
|
||||
case ARCInstKind::AutoreleasepoolPush:
|
||||
case ARCInstKind::None:
|
||||
return false;
|
||||
default:
|
||||
return CanAlterRefCount(Inst, Arg, PA, Class);
|
||||
|
@ -162,14 +161,14 @@ llvm::objcarc::Depends(DependenceKind Flavor, Instruction *Inst,
|
|||
}
|
||||
|
||||
case RetainAutoreleaseDep:
|
||||
switch (GetBasicInstructionClass(Inst)) {
|
||||
case IC_AutoreleasepoolPop:
|
||||
case IC_AutoreleasepoolPush:
|
||||
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 IC_Retain:
|
||||
case IC_RetainRV:
|
||||
case ARCInstKind::Retain:
|
||||
case ARCInstKind::RetainRV:
|
||||
// Check for a retain of the same pointer for merging.
|
||||
return GetArgRCIdentityRoot(Inst) == Arg;
|
||||
default:
|
||||
|
@ -178,10 +177,10 @@ llvm::objcarc::Depends(DependenceKind Flavor, Instruction *Inst,
|
|||
}
|
||||
|
||||
case RetainAutoreleaseRVDep: {
|
||||
InstructionClass Class = GetBasicInstructionClass(Inst);
|
||||
ARCInstKind Class = GetBasicARCInstKind(Inst);
|
||||
switch (Class) {
|
||||
case IC_Retain:
|
||||
case IC_RetainRV:
|
||||
case ARCInstKind::Retain:
|
||||
case ARCInstKind::RetainRV:
|
||||
// Check for a retain of the same pointer for merging.
|
||||
return GetArgRCIdentityRoot(Inst) == Arg;
|
||||
default:
|
||||
|
@ -192,7 +191,7 @@ llvm::objcarc::Depends(DependenceKind Flavor, Instruction *Inst,
|
|||
}
|
||||
|
||||
case RetainRVDep:
|
||||
return CanInterruptRV(GetBasicInstructionClass(Inst));
|
||||
return CanInterruptRV(GetBasicARCInstKind(Inst));
|
||||
}
|
||||
|
||||
llvm_unreachable("Invalid dependence flavor");
|
||||
|
|
|
@ -63,15 +63,13 @@ Depends(DependenceKind Flavor, Instruction *Inst, const Value *Arg,
|
|||
|
||||
/// Test whether the given instruction can "use" the given pointer's object in a
|
||||
/// way that requires the reference count to be positive.
|
||||
bool
|
||||
CanUse(const Instruction *Inst, const Value *Ptr, ProvenanceAnalysis &PA,
|
||||
InstructionClass Class);
|
||||
bool CanUse(const Instruction *Inst, const Value *Ptr, ProvenanceAnalysis &PA,
|
||||
ARCInstKind Class);
|
||||
|
||||
/// Test whether the given instruction can result in a reference count
|
||||
/// modification (positive or negative) for the pointer's object.
|
||||
bool
|
||||
CanAlterRefCount(const Instruction *Inst, const Value *Ptr,
|
||||
ProvenanceAnalysis &PA, InstructionClass Class);
|
||||
bool CanAlterRefCount(const Instruction *Inst, const Value *Ptr,
|
||||
ProvenanceAnalysis &PA, ARCInstKind Class);
|
||||
|
||||
} // namespace objcarc
|
||||
} // namespace llvm
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
#include "llvm/Pass.h"
|
||||
#include "llvm/Transforms/ObjCARC.h"
|
||||
#include "llvm/Transforms/Utils/Local.h"
|
||||
#include "ARCInstKind.h"
|
||||
|
||||
namespace llvm {
|
||||
class raw_ostream;
|
||||
|
@ -68,160 +69,13 @@ static inline bool ModuleHasARC(const Module &M) {
|
|||
M.getNamedValue("clang.arc.use");
|
||||
}
|
||||
|
||||
/// \enum InstructionClass
|
||||
/// \brief A simple classification for instructions.
|
||||
enum InstructionClass {
|
||||
IC_Retain, ///< objc_retain
|
||||
IC_RetainRV, ///< objc_retainAutoreleasedReturnValue
|
||||
IC_RetainBlock, ///< objc_retainBlock
|
||||
IC_Release, ///< objc_release
|
||||
IC_Autorelease, ///< objc_autorelease
|
||||
IC_AutoreleaseRV, ///< objc_autoreleaseReturnValue
|
||||
IC_AutoreleasepoolPush, ///< objc_autoreleasePoolPush
|
||||
IC_AutoreleasepoolPop, ///< objc_autoreleasePoolPop
|
||||
IC_NoopCast, ///< objc_retainedObject, etc.
|
||||
IC_FusedRetainAutorelease, ///< objc_retainAutorelease
|
||||
IC_FusedRetainAutoreleaseRV, ///< objc_retainAutoreleaseReturnValue
|
||||
IC_LoadWeakRetained, ///< objc_loadWeakRetained (primitive)
|
||||
IC_StoreWeak, ///< objc_storeWeak (primitive)
|
||||
IC_InitWeak, ///< objc_initWeak (derived)
|
||||
IC_LoadWeak, ///< objc_loadWeak (derived)
|
||||
IC_MoveWeak, ///< objc_moveWeak (derived)
|
||||
IC_CopyWeak, ///< objc_copyWeak (derived)
|
||||
IC_DestroyWeak, ///< objc_destroyWeak (derived)
|
||||
IC_StoreStrong, ///< objc_storeStrong (derived)
|
||||
IC_IntrinsicUser, ///< clang.arc.use
|
||||
IC_CallOrUser, ///< could call objc_release and/or "use" pointers
|
||||
IC_Call, ///< could call objc_release
|
||||
IC_User, ///< could "use" a pointer
|
||||
IC_None ///< anything else
|
||||
};
|
||||
|
||||
raw_ostream &operator<<(raw_ostream &OS, const InstructionClass Class);
|
||||
|
||||
/// \brief Test if the given class is a kind of user.
|
||||
inline static bool IsUser(InstructionClass Class) {
|
||||
return Class == IC_User ||
|
||||
Class == IC_CallOrUser ||
|
||||
Class == IC_IntrinsicUser;
|
||||
}
|
||||
|
||||
/// \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) {
|
||||
return Class == IC_Retain ||
|
||||
Class == IC_RetainRV ||
|
||||
Class == IC_Autorelease ||
|
||||
Class == IC_AutoreleaseRV ||
|
||||
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;
|
||||
}
|
||||
|
||||
/// Test whether the given instruction can autorelease any pointer or cause an
|
||||
/// autoreleasepool pop.
|
||||
static inline bool
|
||||
CanInterruptRV(InstructionClass Class) {
|
||||
switch (Class) {
|
||||
case IC_AutoreleasepoolPop:
|
||||
case IC_CallOrUser:
|
||||
case IC_Call:
|
||||
case IC_Autorelease:
|
||||
case IC_AutoreleaseRV:
|
||||
case IC_FusedRetainAutorelease:
|
||||
case IC_FusedRetainAutoreleaseRV:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// \brief Determine if F is one of the special known Functions. If it isn't,
|
||||
/// return IC_CallOrUser.
|
||||
InstructionClass GetFunctionClass(const Function *F);
|
||||
|
||||
/// \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 inline InstructionClass GetBasicInstructionClass(const Value *V) {
|
||||
if (const CallInst *CI = dyn_cast<CallInst>(V)) {
|
||||
if (const Function *F = CI->getCalledFunction())
|
||||
return GetFunctionClass(F);
|
||||
// Otherwise, be conservative.
|
||||
return IC_CallOrUser;
|
||||
}
|
||||
|
||||
// Otherwise, be conservative.
|
||||
return isa<InvokeInst>(V) ? IC_CallOrUser : IC_User;
|
||||
}
|
||||
|
||||
/// \brief Determine what kind of construct V is.
|
||||
InstructionClass GetInstructionClass(const Value *V);
|
||||
|
||||
/// \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)))
|
||||
if (!IsForwarding(GetBasicARCInstKind(V)))
|
||||
break;
|
||||
V = cast<CallInst>(V)->getArgOperand(0);
|
||||
}
|
||||
|
@ -247,7 +101,7 @@ static inline const Value *GetUnderlyingObjCPtr(const Value *V) {
|
|||
static inline const Value *GetRCIdentityRoot(const Value *V) {
|
||||
for (;;) {
|
||||
V = V->stripPointerCasts();
|
||||
if (!IsForwarding(GetBasicInstructionClass(V)))
|
||||
if (!IsForwarding(GetBasicARCInstKind(V)))
|
||||
break;
|
||||
V = cast<CallInst>(V)->getArgOperand(0);
|
||||
}
|
||||
|
@ -293,8 +147,8 @@ static inline void EraseInstruction(Instruction *CI) {
|
|||
|
||||
if (!Unused) {
|
||||
// Replace the return value with the argument.
|
||||
assert((IsForwarding(GetBasicInstructionClass(CI)) ||
|
||||
(IsNoopOnNull(GetBasicInstructionClass(CI)) &&
|
||||
assert((IsForwarding(GetBasicARCInstKind(CI)) ||
|
||||
(IsNoopOnNull(GetBasicARCInstKind(CI)) &&
|
||||
isa<ConstantPointerNull>(OldArg))) &&
|
||||
"Can't delete non-forwarding instruction with users!");
|
||||
CI->replaceAllUsesWith(OldArg);
|
||||
|
@ -351,15 +205,15 @@ static inline bool IsPotentialRetainableObjPtr(const Value *Op,
|
|||
return true;
|
||||
}
|
||||
|
||||
/// \brief Helper for GetInstructionClass. Determines what kind of construct CS
|
||||
/// \brief Helper for GetARCInstKind. Determines what kind of construct CS
|
||||
/// is.
|
||||
static inline InstructionClass GetCallSiteClass(ImmutableCallSite CS) {
|
||||
static inline ARCInstKind GetCallSiteClass(ImmutableCallSite CS) {
|
||||
for (ImmutableCallSite::arg_iterator I = CS.arg_begin(), E = CS.arg_end();
|
||||
I != E; ++I)
|
||||
if (IsPotentialRetainableObjPtr(*I))
|
||||
return CS.onlyReadsMemory() ? IC_User : IC_CallOrUser;
|
||||
return CS.onlyReadsMemory() ? ARCInstKind::User : ARCInstKind::CallOrUser;
|
||||
|
||||
return CS.onlyReadsMemory() ? IC_None : IC_Call;
|
||||
return CS.onlyReadsMemory() ? ARCInstKind::None : ARCInstKind::Call;
|
||||
}
|
||||
|
||||
/// \brief Return true if this value refers to a distinct and identifiable
|
||||
|
|
|
@ -97,11 +97,11 @@ bool ObjCARCAPElim::OptimizeBB(BasicBlock *BB) {
|
|||
Instruction *Push = nullptr;
|
||||
for (BasicBlock::iterator I = BB->begin(), E = BB->end(); I != E; ) {
|
||||
Instruction *Inst = I++;
|
||||
switch (GetBasicInstructionClass(Inst)) {
|
||||
case IC_AutoreleasepoolPush:
|
||||
switch (GetBasicARCInstKind(Inst)) {
|
||||
case ARCInstKind::AutoreleasepoolPush:
|
||||
Push = Inst;
|
||||
break;
|
||||
case IC_AutoreleasepoolPop:
|
||||
case ARCInstKind::AutoreleasepoolPop:
|
||||
// If this pop matches a push and nothing in between can autorelease,
|
||||
// zap the pair.
|
||||
if (Push && cast<CallInst>(Inst)->getArgOperand(0) == Push) {
|
||||
|
@ -115,7 +115,7 @@ bool ObjCARCAPElim::OptimizeBB(BasicBlock *BB) {
|
|||
}
|
||||
Push = nullptr;
|
||||
break;
|
||||
case IC_CallOrUser:
|
||||
case ARCInstKind::CallOrUser:
|
||||
if (MayAutorelease(ImmutableCallSite(Inst)))
|
||||
Push = nullptr;
|
||||
break;
|
||||
|
|
|
@ -120,7 +120,7 @@ ObjCARCAliasAnalysis::getModRefBehavior(const Function *F) {
|
|||
return AliasAnalysis::getModRefBehavior(F);
|
||||
|
||||
switch (GetFunctionClass(F)) {
|
||||
case IC_NoopCast:
|
||||
case ARCInstKind::NoopCast:
|
||||
return DoesNotAccessMemory;
|
||||
default:
|
||||
break;
|
||||
|
@ -134,15 +134,15 @@ 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:
|
||||
switch (GetBasicARCInstKind(CS.getInstruction())) {
|
||||
case ARCInstKind::Retain:
|
||||
case ARCInstKind::RetainRV:
|
||||
case ARCInstKind::Autorelease:
|
||||
case ARCInstKind::AutoreleaseRV:
|
||||
case ARCInstKind::NoopCast:
|
||||
case ARCInstKind::AutoreleasepoolPush:
|
||||
case ARCInstKind::FusedRetainAutorelease:
|
||||
case ARCInstKind::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.
|
||||
|
|
|
@ -83,7 +83,7 @@ namespace {
|
|||
|
||||
bool
|
||||
contractAutorelease(Function &F, Instruction *Autorelease,
|
||||
InstructionClass Class,
|
||||
ARCInstKind Class,
|
||||
SmallPtrSetImpl<Instruction *> &DependingInstructions,
|
||||
SmallPtrSetImpl<const BasicBlock *> &Visited);
|
||||
|
||||
|
@ -143,7 +143,7 @@ bool ObjCARCContract::optimizeRetainCall(Function &F, Instruction *Retain) {
|
|||
|
||||
/// Merge an autorelease with a retain into a fused call.
|
||||
bool ObjCARCContract::contractAutorelease(
|
||||
Function &F, Instruction *Autorelease, InstructionClass Class,
|
||||
Function &F, Instruction *Autorelease, ARCInstKind Class,
|
||||
SmallPtrSetImpl<Instruction *> &DependingInstructions,
|
||||
SmallPtrSetImpl<const BasicBlock *> &Visited) {
|
||||
const Value *Arg = GetArgRCIdentityRoot(Autorelease);
|
||||
|
@ -151,7 +151,7 @@ bool ObjCARCContract::contractAutorelease(
|
|||
// Check that there are no instructions between the retain and the autorelease
|
||||
// (such as an autorelease_pop) which may change the count.
|
||||
CallInst *Retain = nullptr;
|
||||
if (Class == IC_AutoreleaseRV)
|
||||
if (Class == ARCInstKind::AutoreleaseRV)
|
||||
FindDependencies(RetainAutoreleaseRVDep, Arg,
|
||||
Autorelease->getParent(), Autorelease,
|
||||
DependingInstructions, Visited, PA);
|
||||
|
@ -169,8 +169,7 @@ bool ObjCARCContract::contractAutorelease(
|
|||
Retain = dyn_cast_or_null<CallInst>(*DependingInstructions.begin());
|
||||
DependingInstructions.clear();
|
||||
|
||||
if (!Retain ||
|
||||
GetBasicInstructionClass(Retain) != IC_Retain ||
|
||||
if (!Retain || GetBasicARCInstKind(Retain) != ARCInstKind::Retain ||
|
||||
GetArgRCIdentityRoot(Retain) != Arg)
|
||||
return false;
|
||||
|
||||
|
@ -181,9 +180,9 @@ bool ObjCARCContract::contractAutorelease(
|
|||
" Autorelease:" << *Autorelease << "\n"
|
||||
" Retain: " << *Retain << "\n");
|
||||
|
||||
Constant *Decl = EP.get(Class == IC_AutoreleaseRV ?
|
||||
ARCRuntimeEntryPoints::EPT_RetainAutoreleaseRV :
|
||||
ARCRuntimeEntryPoints::EPT_RetainAutorelease);
|
||||
Constant *Decl = EP.get(Class == ARCInstKind::AutoreleaseRV
|
||||
? ARCRuntimeEntryPoints::EPT_RetainAutoreleaseRV
|
||||
: ARCRuntimeEntryPoints::EPT_RetainAutorelease);
|
||||
Retain->setCalledFunction(Decl);
|
||||
|
||||
DEBUG(dbgs() << " New RetainAutorelease: " << *Retain << "\n");
|
||||
|
@ -222,7 +221,7 @@ tryToContractReleaseIntoStoreStrong(Instruction *Release, inst_iterator &Iter) {
|
|||
continue;
|
||||
}
|
||||
|
||||
InstructionClass Class = GetBasicInstructionClass(Inst);
|
||||
ARCInstKind Class = GetBasicARCInstKind(Inst);
|
||||
|
||||
// Unrelated retains are harmless.
|
||||
if (IsRetain(Class))
|
||||
|
@ -247,10 +246,11 @@ tryToContractReleaseIntoStoreStrong(Instruction *Release, inst_iterator &Iter) {
|
|||
// Walk up to find the retain.
|
||||
I = Store;
|
||||
BasicBlock::iterator Begin = BB->begin();
|
||||
while (I != Begin && GetBasicInstructionClass(I) != IC_Retain)
|
||||
while (I != Begin && GetBasicARCInstKind(I) != ARCInstKind::Retain)
|
||||
--I;
|
||||
Instruction *Retain = I;
|
||||
if (GetBasicInstructionClass(Retain) != IC_Retain) return;
|
||||
if (GetBasicARCInstKind(Retain) != ARCInstKind::Retain)
|
||||
return;
|
||||
if (GetArgRCIdentityRoot(Retain) != New) return;
|
||||
|
||||
Changed = true;
|
||||
|
@ -300,22 +300,22 @@ bool ObjCARCContract::tryToPeepholeInstruction(
|
|||
bool &TailOkForStoreStrongs) {
|
||||
// Only these library routines return their argument. In particular,
|
||||
// objc_retainBlock does not necessarily return its argument.
|
||||
InstructionClass Class = GetBasicInstructionClass(Inst);
|
||||
ARCInstKind Class = GetBasicARCInstKind(Inst);
|
||||
switch (Class) {
|
||||
case IC_FusedRetainAutorelease:
|
||||
case IC_FusedRetainAutoreleaseRV:
|
||||
case ARCInstKind::FusedRetainAutorelease:
|
||||
case ARCInstKind::FusedRetainAutoreleaseRV:
|
||||
return false;
|
||||
case IC_Autorelease:
|
||||
case IC_AutoreleaseRV:
|
||||
case ARCInstKind::Autorelease:
|
||||
case ARCInstKind::AutoreleaseRV:
|
||||
return contractAutorelease(F, Inst, Class, DependingInsts, Visited);
|
||||
case IC_Retain:
|
||||
case ARCInstKind::Retain:
|
||||
// Attempt to convert retains to retainrvs if they are next to function
|
||||
// calls.
|
||||
if (!optimizeRetainCall(F, Inst))
|
||||
return false;
|
||||
// If we succeed in our optimization, fall through.
|
||||
// FALLTHROUGH
|
||||
case IC_RetainRV: {
|
||||
case ARCInstKind::RetainRV: {
|
||||
// If we're compiling for a target which needs a special inline-asm
|
||||
// marker to do the retainAutoreleasedReturnValue optimization,
|
||||
// insert it now.
|
||||
|
@ -352,7 +352,7 @@ bool ObjCARCContract::tryToPeepholeInstruction(
|
|||
decline_rv_optimization:
|
||||
return false;
|
||||
}
|
||||
case IC_InitWeak: {
|
||||
case ARCInstKind::InitWeak: {
|
||||
// objc_initWeak(p, null) => *p = null
|
||||
CallInst *CI = cast<CallInst>(Inst);
|
||||
if (IsNullOrUndef(CI->getArgOperand(1))) {
|
||||
|
@ -369,19 +369,19 @@ bool ObjCARCContract::tryToPeepholeInstruction(
|
|||
}
|
||||
return true;
|
||||
}
|
||||
case IC_Release:
|
||||
case ARCInstKind::Release:
|
||||
// Try to form an objc store strong from our release. If we fail, there is
|
||||
// nothing further to do below, so continue.
|
||||
tryToContractReleaseIntoStoreStrong(Inst, Iter);
|
||||
return true;
|
||||
case IC_User:
|
||||
case ARCInstKind::User:
|
||||
// Be conservative if the function has any alloca instructions.
|
||||
// Technically we only care about escaping alloca instructions,
|
||||
// but this is sufficient to handle some interesting cases.
|
||||
if (isa<AllocaInst>(Inst))
|
||||
TailOkForStoreStrongs = false;
|
||||
return true;
|
||||
case IC_IntrinsicUser:
|
||||
case ARCInstKind::IntrinsicUser:
|
||||
// Remove calls to @clang.arc.use(...).
|
||||
Inst->eraseFromParent();
|
||||
return true;
|
||||
|
|
|
@ -99,13 +99,13 @@ bool ObjCARCExpand::runOnFunction(Function &F) {
|
|||
|
||||
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: {
|
||||
switch (GetBasicARCInstKind(Inst)) {
|
||||
case ARCInstKind::Retain:
|
||||
case ARCInstKind::RetainRV:
|
||||
case ARCInstKind::Autorelease:
|
||||
case ARCInstKind::AutoreleaseRV:
|
||||
case ARCInstKind::FusedRetainAutorelease:
|
||||
case ARCInstKind::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
|
||||
|
|
|
@ -153,7 +153,7 @@ static const Value *FindSingleUseIdentifiedObject(const Value *Arg) {
|
|||
if (const GetElementPtrInst *GEP = dyn_cast<GetElementPtrInst>(Arg))
|
||||
if (GEP->hasAllZeroIndices())
|
||||
return FindSingleUseIdentifiedObject(GEP->getPointerOperand());
|
||||
if (IsForwarding(GetBasicInstructionClass(Arg)))
|
||||
if (IsForwarding(GetBasicARCInstKind(Arg)))
|
||||
return FindSingleUseIdentifiedObject(
|
||||
cast<CallInst>(Arg)->getArgOperand(0));
|
||||
if (!IsObjCIdentifiedObject(Arg))
|
||||
|
@ -1096,7 +1096,7 @@ namespace {
|
|||
|
||||
bool OptimizeRetainRVCall(Function &F, Instruction *RetainRV);
|
||||
void OptimizeAutoreleaseRVCall(Function &F, Instruction *AutoreleaseRV,
|
||||
InstructionClass &Class);
|
||||
ARCInstKind &Class);
|
||||
void OptimizeIndividualCalls(Function &F);
|
||||
|
||||
void CheckForCFGHazards(const BasicBlock *BB,
|
||||
|
@ -1216,7 +1216,7 @@ ObjCARCOpt::OptimizeRetainRVCall(Function &F, Instruction *RetainRV) {
|
|||
BasicBlock::iterator I = RetainRV, Begin = RetainRV->getParent()->begin();
|
||||
if (I != Begin) {
|
||||
do --I; while (I != Begin && IsNoopInstruction(I));
|
||||
if (GetBasicInstructionClass(I) == IC_AutoreleaseRV &&
|
||||
if (GetBasicARCInstKind(I) == ARCInstKind::AutoreleaseRV &&
|
||||
GetArgRCIdentityRoot(I) == Arg) {
|
||||
Changed = true;
|
||||
++NumPeeps;
|
||||
|
@ -1248,9 +1248,9 @@ ObjCARCOpt::OptimizeRetainRVCall(Function &F, Instruction *RetainRV) {
|
|||
|
||||
/// Turn objc_autoreleaseReturnValue into objc_autorelease if the result is not
|
||||
/// used as a return value.
|
||||
void
|
||||
ObjCARCOpt::OptimizeAutoreleaseRVCall(Function &F, Instruction *AutoreleaseRV,
|
||||
InstructionClass &Class) {
|
||||
void ObjCARCOpt::OptimizeAutoreleaseRVCall(Function &F,
|
||||
Instruction *AutoreleaseRV,
|
||||
ARCInstKind &Class) {
|
||||
// Check for a return of the pointer value.
|
||||
const Value *Ptr = GetArgRCIdentityRoot(AutoreleaseRV);
|
||||
SmallVector<const Value *, 2> Users;
|
||||
|
@ -1258,7 +1258,7 @@ ObjCARCOpt::OptimizeAutoreleaseRVCall(Function &F, Instruction *AutoreleaseRV,
|
|||
do {
|
||||
Ptr = Users.pop_back_val();
|
||||
for (const User *U : Ptr->users()) {
|
||||
if (isa<ReturnInst>(U) || GetBasicInstructionClass(U) == IC_RetainRV)
|
||||
if (isa<ReturnInst>(U) || GetBasicARCInstKind(U) == ARCInstKind::RetainRV)
|
||||
return;
|
||||
if (isa<BitCastInst>(U))
|
||||
Users.push_back(U);
|
||||
|
@ -1277,7 +1277,7 @@ ObjCARCOpt::OptimizeAutoreleaseRVCall(Function &F, Instruction *AutoreleaseRV,
|
|||
Constant *NewDecl = EP.get(ARCRuntimeEntryPoints::EPT_Autorelease);
|
||||
AutoreleaseRVCI->setCalledFunction(NewDecl);
|
||||
AutoreleaseRVCI->setTailCall(false); // Never tail call objc_autorelease.
|
||||
Class = IC_Autorelease;
|
||||
Class = ARCInstKind::Autorelease;
|
||||
|
||||
DEBUG(dbgs() << "New: " << *AutoreleaseRV << "\n");
|
||||
|
||||
|
@ -1294,7 +1294,7 @@ void ObjCARCOpt::OptimizeIndividualCalls(Function &F) {
|
|||
for (inst_iterator I = inst_begin(&F), E = inst_end(&F); I != E; ) {
|
||||
Instruction *Inst = &*I++;
|
||||
|
||||
InstructionClass Class = GetBasicInstructionClass(Inst);
|
||||
ARCInstKind Class = GetBasicARCInstKind(Inst);
|
||||
|
||||
DEBUG(dbgs() << "Visiting: Class: " << Class << "; " << *Inst << "\n");
|
||||
|
||||
|
@ -1309,7 +1309,7 @@ void ObjCARCOpt::OptimizeIndividualCalls(Function &F) {
|
|||
// There are gray areas here, as the ability to cast reference-counted
|
||||
// pointers to raw void* and back allows code to break ARC assumptions,
|
||||
// however these are currently considered to be unimportant.
|
||||
case IC_NoopCast:
|
||||
case ARCInstKind::NoopCast:
|
||||
Changed = true;
|
||||
++NumNoops;
|
||||
DEBUG(dbgs() << "Erasing no-op cast: " << *Inst << "\n");
|
||||
|
@ -1317,11 +1317,11 @@ void ObjCARCOpt::OptimizeIndividualCalls(Function &F) {
|
|||
continue;
|
||||
|
||||
// If the pointer-to-weak-pointer is null, it's undefined behavior.
|
||||
case IC_StoreWeak:
|
||||
case IC_LoadWeak:
|
||||
case IC_LoadWeakRetained:
|
||||
case IC_InitWeak:
|
||||
case IC_DestroyWeak: {
|
||||
case ARCInstKind::StoreWeak:
|
||||
case ARCInstKind::LoadWeak:
|
||||
case ARCInstKind::LoadWeakRetained:
|
||||
case ARCInstKind::InitWeak:
|
||||
case ARCInstKind::DestroyWeak: {
|
||||
CallInst *CI = cast<CallInst>(Inst);
|
||||
if (IsNullOrUndef(CI->getArgOperand(0))) {
|
||||
Changed = true;
|
||||
|
@ -1338,8 +1338,8 @@ void ObjCARCOpt::OptimizeIndividualCalls(Function &F) {
|
|||
}
|
||||
break;
|
||||
}
|
||||
case IC_CopyWeak:
|
||||
case IC_MoveWeak: {
|
||||
case ARCInstKind::CopyWeak:
|
||||
case ARCInstKind::MoveWeak: {
|
||||
CallInst *CI = cast<CallInst>(Inst);
|
||||
if (IsNullOrUndef(CI->getArgOperand(0)) ||
|
||||
IsNullOrUndef(CI->getArgOperand(1))) {
|
||||
|
@ -1359,11 +1359,11 @@ void ObjCARCOpt::OptimizeIndividualCalls(Function &F) {
|
|||
}
|
||||
break;
|
||||
}
|
||||
case IC_RetainRV:
|
||||
case ARCInstKind::RetainRV:
|
||||
if (OptimizeRetainRVCall(F, Inst))
|
||||
continue;
|
||||
break;
|
||||
case IC_AutoreleaseRV:
|
||||
case ARCInstKind::AutoreleaseRV:
|
||||
OptimizeAutoreleaseRVCall(F, Inst, Class);
|
||||
break;
|
||||
}
|
||||
|
@ -1391,7 +1391,7 @@ void ObjCARCOpt::OptimizeIndividualCalls(Function &F) {
|
|||
|
||||
EraseInstruction(Call);
|
||||
Inst = NewCall;
|
||||
Class = IC_Release;
|
||||
Class = ARCInstKind::Release;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1422,7 +1422,7 @@ void ObjCARCOpt::OptimizeIndividualCalls(Function &F) {
|
|||
}
|
||||
|
||||
if (!IsNoopOnNull(Class)) {
|
||||
UsedInThisFunction |= 1 << Class;
|
||||
UsedInThisFunction |= 1 << unsigned(Class);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -1440,7 +1440,7 @@ void ObjCARCOpt::OptimizeIndividualCalls(Function &F) {
|
|||
|
||||
// Keep track of which of retain, release, autorelease, and retain_block
|
||||
// are actually present in this function.
|
||||
UsedInThisFunction |= 1 << Class;
|
||||
UsedInThisFunction |= 1 << unsigned(Class);
|
||||
|
||||
// If Arg is a PHI, and one or more incoming values to the
|
||||
// PHI are null, and the call is control-equivalent to the PHI, and there
|
||||
|
@ -1480,25 +1480,25 @@ void ObjCARCOpt::OptimizeIndividualCalls(Function &F) {
|
|||
// Check that there is nothing that cares about the reference
|
||||
// count between the call and the phi.
|
||||
switch (Class) {
|
||||
case IC_Retain:
|
||||
case IC_RetainBlock:
|
||||
case ARCInstKind::Retain:
|
||||
case ARCInstKind::RetainBlock:
|
||||
// These can always be moved up.
|
||||
break;
|
||||
case IC_Release:
|
||||
case ARCInstKind::Release:
|
||||
// These can't be moved across things that care about the retain
|
||||
// count.
|
||||
FindDependencies(NeedsPositiveRetainCount, Arg,
|
||||
Inst->getParent(), Inst,
|
||||
DependingInstructions, Visited, PA);
|
||||
break;
|
||||
case IC_Autorelease:
|
||||
case ARCInstKind::Autorelease:
|
||||
// These can't be moved across autorelease pool scope boundaries.
|
||||
FindDependencies(AutoreleasePoolBoundary, Arg,
|
||||
Inst->getParent(), Inst,
|
||||
DependingInstructions, Visited, PA);
|
||||
break;
|
||||
case IC_RetainRV:
|
||||
case IC_AutoreleaseRV:
|
||||
case ARCInstKind::RetainRV:
|
||||
case ARCInstKind::AutoreleaseRV:
|
||||
// Don't move these; the RV optimization depends on the autoreleaseRV
|
||||
// being tail called, and the retainRV being immediately after a call
|
||||
// (which might still happen if we get lucky with codegen layout, but
|
||||
|
@ -1711,13 +1711,13 @@ ObjCARCOpt::VisitInstructionBottomUp(Instruction *Inst,
|
|||
MapVector<Value *, RRInfo> &Retains,
|
||||
BBState &MyStates) {
|
||||
bool NestingDetected = false;
|
||||
InstructionClass Class = GetInstructionClass(Inst);
|
||||
ARCInstKind Class = GetARCInstKind(Inst);
|
||||
const Value *Arg = nullptr;
|
||||
|
||||
DEBUG(dbgs() << "Class: " << Class << "\n");
|
||||
|
||||
switch (Class) {
|
||||
case IC_Release: {
|
||||
case ARCInstKind::Release: {
|
||||
Arg = GetArgRCIdentityRoot(Inst);
|
||||
|
||||
PtrState &S = MyStates.getPtrBottomUpState(Arg);
|
||||
|
@ -1745,13 +1745,13 @@ ObjCARCOpt::VisitInstructionBottomUp(Instruction *Inst,
|
|||
S.SetKnownPositiveRefCount();
|
||||
break;
|
||||
}
|
||||
case IC_RetainBlock:
|
||||
case ARCInstKind::RetainBlock:
|
||||
// In OptimizeIndividualCalls, we have strength reduced all optimizable
|
||||
// objc_retainBlocks to objc_retains. Thus at this point any
|
||||
// objc_retainBlocks that we see are not optimizable.
|
||||
break;
|
||||
case IC_Retain:
|
||||
case IC_RetainRV: {
|
||||
case ARCInstKind::Retain:
|
||||
case ARCInstKind::RetainRV: {
|
||||
Arg = GetArgRCIdentityRoot(Inst);
|
||||
|
||||
PtrState &S = MyStates.getPtrBottomUpState(Arg);
|
||||
|
@ -1769,9 +1769,10 @@ ObjCARCOpt::VisitInstructionBottomUp(Instruction *Inst,
|
|||
S.ClearReverseInsertPts();
|
||||
// FALL THROUGH
|
||||
case S_CanRelease:
|
||||
// Don't do retain+release tracking for IC_RetainRV, because it's
|
||||
// Don't do retain+release tracking for ARCInstKind::RetainRV,
|
||||
// because it's
|
||||
// better to let it remain as the first instruction after a call.
|
||||
if (Class != IC_RetainRV)
|
||||
if (Class != ARCInstKind::RetainRV)
|
||||
Retains[Inst] = S.GetRRInfo();
|
||||
S.ClearSequenceProgress();
|
||||
break;
|
||||
|
@ -1784,15 +1785,15 @@ ObjCARCOpt::VisitInstructionBottomUp(Instruction *Inst,
|
|||
// A retain moving bottom up can be a use.
|
||||
break;
|
||||
}
|
||||
case IC_AutoreleasepoolPop:
|
||||
case ARCInstKind::AutoreleasepoolPop:
|
||||
// Conservatively, clear MyStates for all known pointers.
|
||||
MyStates.clearBottomUpPointers();
|
||||
return NestingDetected;
|
||||
case IC_AutoreleasepoolPush:
|
||||
case IC_None:
|
||||
case ARCInstKind::AutoreleasepoolPush:
|
||||
case ARCInstKind::None:
|
||||
// These are irrelevant.
|
||||
return NestingDetected;
|
||||
case IC_User:
|
||||
case ARCInstKind::User:
|
||||
// If we have a store into an alloca of a pointer we are tracking, the
|
||||
// pointer has multiple owners implying that we must be more conservative.
|
||||
//
|
||||
|
@ -1967,24 +1968,25 @@ ObjCARCOpt::VisitInstructionTopDown(Instruction *Inst,
|
|||
DenseMap<Value *, RRInfo> &Releases,
|
||||
BBState &MyStates) {
|
||||
bool NestingDetected = false;
|
||||
InstructionClass Class = GetInstructionClass(Inst);
|
||||
ARCInstKind Class = GetARCInstKind(Inst);
|
||||
const Value *Arg = nullptr;
|
||||
|
||||
switch (Class) {
|
||||
case IC_RetainBlock:
|
||||
case ARCInstKind::RetainBlock:
|
||||
// In OptimizeIndividualCalls, we have strength reduced all optimizable
|
||||
// objc_retainBlocks to objc_retains. Thus at this point any
|
||||
// objc_retainBlocks that we see are not optimizable.
|
||||
break;
|
||||
case IC_Retain:
|
||||
case IC_RetainRV: {
|
||||
case ARCInstKind::Retain:
|
||||
case ARCInstKind::RetainRV: {
|
||||
Arg = GetArgRCIdentityRoot(Inst);
|
||||
|
||||
PtrState &S = MyStates.getPtrTopDownState(Arg);
|
||||
|
||||
// Don't do retain+release tracking for IC_RetainRV, because it's
|
||||
// Don't do retain+release tracking for ARCInstKind::RetainRV, because
|
||||
// it's
|
||||
// better to let it remain as the first instruction after a call.
|
||||
if (Class != IC_RetainRV) {
|
||||
if (Class != ARCInstKind::RetainRV) {
|
||||
// If we see two retains in a row on the same pointer. If so, make
|
||||
// a note, and we'll cicle back to revisit it after we've
|
||||
// hopefully eliminated the second retain, which may allow us to
|
||||
|
@ -2007,7 +2009,7 @@ ObjCARCOpt::VisitInstructionTopDown(Instruction *Inst,
|
|||
// code below.
|
||||
break;
|
||||
}
|
||||
case IC_Release: {
|
||||
case ARCInstKind::Release: {
|
||||
Arg = GetArgRCIdentityRoot(Inst);
|
||||
|
||||
PtrState &S = MyStates.getPtrTopDownState(Arg);
|
||||
|
@ -2039,12 +2041,12 @@ ObjCARCOpt::VisitInstructionTopDown(Instruction *Inst,
|
|||
}
|
||||
break;
|
||||
}
|
||||
case IC_AutoreleasepoolPop:
|
||||
case ARCInstKind::AutoreleasepoolPop:
|
||||
// Conservatively, clear MyStates for all known pointers.
|
||||
MyStates.clearTopDownPointers();
|
||||
return NestingDetected;
|
||||
case IC_AutoreleasepoolPush:
|
||||
case IC_None:
|
||||
case ARCInstKind::AutoreleasepoolPush:
|
||||
case ARCInstKind::None:
|
||||
// These are irrelevant.
|
||||
return NestingDetected;
|
||||
default:
|
||||
|
@ -2640,12 +2642,13 @@ void ObjCARCOpt::OptimizeWeakCalls(Function &F) {
|
|||
|
||||
DEBUG(dbgs() << "Visiting: " << *Inst << "\n");
|
||||
|
||||
InstructionClass Class = GetBasicInstructionClass(Inst);
|
||||
if (Class != IC_LoadWeak && Class != IC_LoadWeakRetained)
|
||||
ARCInstKind Class = GetBasicARCInstKind(Inst);
|
||||
if (Class != ARCInstKind::LoadWeak &&
|
||||
Class != ARCInstKind::LoadWeakRetained)
|
||||
continue;
|
||||
|
||||
// Delete objc_loadWeak calls with no users.
|
||||
if (Class == IC_LoadWeak && Inst->use_empty()) {
|
||||
if (Class == ARCInstKind::LoadWeak && Inst->use_empty()) {
|
||||
Inst->eraseFromParent();
|
||||
continue;
|
||||
}
|
||||
|
@ -2660,10 +2663,10 @@ void ObjCARCOpt::OptimizeWeakCalls(Function &F) {
|
|||
J = Current.getInstructionIterator();
|
||||
J != B; --J) {
|
||||
Instruction *EarlierInst = &*std::prev(J);
|
||||
InstructionClass EarlierClass = GetInstructionClass(EarlierInst);
|
||||
ARCInstKind EarlierClass = GetARCInstKind(EarlierInst);
|
||||
switch (EarlierClass) {
|
||||
case IC_LoadWeak:
|
||||
case IC_LoadWeakRetained: {
|
||||
case ARCInstKind::LoadWeak:
|
||||
case ARCInstKind::LoadWeakRetained: {
|
||||
// If this is loading from the same pointer, replace this load's value
|
||||
// with that one.
|
||||
CallInst *Call = cast<CallInst>(Inst);
|
||||
|
@ -2674,7 +2677,7 @@ void ObjCARCOpt::OptimizeWeakCalls(Function &F) {
|
|||
case AliasAnalysis::MustAlias:
|
||||
Changed = true;
|
||||
// If the load has a builtin retain, insert a plain retain for it.
|
||||
if (Class == IC_LoadWeakRetained) {
|
||||
if (Class == ARCInstKind::LoadWeakRetained) {
|
||||
Constant *Decl = EP.get(ARCRuntimeEntryPoints::EPT_Retain);
|
||||
CallInst *CI = CallInst::Create(Decl, EarlierCall, "", Call);
|
||||
CI->setTailCall();
|
||||
|
@ -2691,8 +2694,8 @@ void ObjCARCOpt::OptimizeWeakCalls(Function &F) {
|
|||
}
|
||||
break;
|
||||
}
|
||||
case IC_StoreWeak:
|
||||
case IC_InitWeak: {
|
||||
case ARCInstKind::StoreWeak:
|
||||
case ARCInstKind::InitWeak: {
|
||||
// If this is storing to the same pointer and has the same size etc.
|
||||
// replace this load's value with the stored value.
|
||||
CallInst *Call = cast<CallInst>(Inst);
|
||||
|
@ -2703,7 +2706,7 @@ void ObjCARCOpt::OptimizeWeakCalls(Function &F) {
|
|||
case AliasAnalysis::MustAlias:
|
||||
Changed = true;
|
||||
// If the load has a builtin retain, insert a plain retain for it.
|
||||
if (Class == IC_LoadWeakRetained) {
|
||||
if (Class == ARCInstKind::LoadWeakRetained) {
|
||||
Constant *Decl = EP.get(ARCRuntimeEntryPoints::EPT_Retain);
|
||||
CallInst *CI = CallInst::Create(Decl, EarlierCall, "", Call);
|
||||
CI->setTailCall();
|
||||
|
@ -2720,14 +2723,14 @@ void ObjCARCOpt::OptimizeWeakCalls(Function &F) {
|
|||
}
|
||||
break;
|
||||
}
|
||||
case IC_MoveWeak:
|
||||
case IC_CopyWeak:
|
||||
case ARCInstKind::MoveWeak:
|
||||
case ARCInstKind::CopyWeak:
|
||||
// TOOD: Grab the copied value.
|
||||
goto clobbered;
|
||||
case IC_AutoreleasepoolPush:
|
||||
case IC_None:
|
||||
case IC_IntrinsicUser:
|
||||
case IC_User:
|
||||
case ARCInstKind::AutoreleasepoolPush:
|
||||
case ARCInstKind::None:
|
||||
case ARCInstKind::IntrinsicUser:
|
||||
case ARCInstKind::User:
|
||||
// Weak pointers are only modified through the weak entry points
|
||||
// (and arbitrary calls, which could call the weak entry points).
|
||||
break;
|
||||
|
@ -2743,8 +2746,8 @@ void ObjCARCOpt::OptimizeWeakCalls(Function &F) {
|
|||
// the alloca and all its users can be zapped.
|
||||
for (inst_iterator I = inst_begin(&F), E = inst_end(&F); I != E; ) {
|
||||
Instruction *Inst = &*I++;
|
||||
InstructionClass Class = GetBasicInstructionClass(Inst);
|
||||
if (Class != IC_DestroyWeak)
|
||||
ARCInstKind Class = GetBasicARCInstKind(Inst);
|
||||
if (Class != ARCInstKind::DestroyWeak)
|
||||
continue;
|
||||
|
||||
CallInst *Call = cast<CallInst>(Inst);
|
||||
|
@ -2752,10 +2755,10 @@ void ObjCARCOpt::OptimizeWeakCalls(Function &F) {
|
|||
if (AllocaInst *Alloca = dyn_cast<AllocaInst>(Arg)) {
|
||||
for (User *U : Alloca->users()) {
|
||||
const Instruction *UserInst = cast<Instruction>(U);
|
||||
switch (GetBasicInstructionClass(UserInst)) {
|
||||
case IC_InitWeak:
|
||||
case IC_StoreWeak:
|
||||
case IC_DestroyWeak:
|
||||
switch (GetBasicARCInstKind(UserInst)) {
|
||||
case ARCInstKind::InitWeak:
|
||||
case ARCInstKind::StoreWeak:
|
||||
case ARCInstKind::DestroyWeak:
|
||||
continue;
|
||||
default:
|
||||
goto done;
|
||||
|
@ -2764,13 +2767,13 @@ void ObjCARCOpt::OptimizeWeakCalls(Function &F) {
|
|||
Changed = true;
|
||||
for (auto UI = Alloca->user_begin(), UE = Alloca->user_end(); UI != UE;) {
|
||||
CallInst *UserInst = cast<CallInst>(*UI++);
|
||||
switch (GetBasicInstructionClass(UserInst)) {
|
||||
case IC_InitWeak:
|
||||
case IC_StoreWeak:
|
||||
switch (GetBasicARCInstKind(UserInst)) {
|
||||
case ARCInstKind::InitWeak:
|
||||
case ARCInstKind::StoreWeak:
|
||||
// These functions return their second argument.
|
||||
UserInst->replaceAllUsesWith(UserInst->getArgOperand(1));
|
||||
break;
|
||||
case IC_DestroyWeak:
|
||||
case ARCInstKind::DestroyWeak:
|
||||
// No return value.
|
||||
break;
|
||||
default:
|
||||
|
@ -2833,8 +2836,8 @@ HasSafePathToPredecessorCall(const Value *Arg, Instruction *Retain,
|
|||
return false;
|
||||
|
||||
// Check that the call is a regular call.
|
||||
InstructionClass Class = GetBasicInstructionClass(Call);
|
||||
if (Class != IC_CallOrUser && Class != IC_Call)
|
||||
ARCInstKind Class = GetBasicARCInstKind(Call);
|
||||
if (Class != ARCInstKind::CallOrUser && Class != ARCInstKind::Call)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
@ -2858,8 +2861,7 @@ FindPredecessorRetainWithSafePath(const Value *Arg, BasicBlock *BB,
|
|||
dyn_cast_or_null<CallInst>(*DepInsts.begin());
|
||||
|
||||
// Check that we found a retain with the same argument.
|
||||
if (!Retain ||
|
||||
!IsRetain(GetBasicInstructionClass(Retain)) ||
|
||||
if (!Retain || !IsRetain(GetBasicARCInstKind(Retain)) ||
|
||||
GetArgRCIdentityRoot(Retain) != Arg) {
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -2885,7 +2887,7 @@ FindPredecessorAutoreleaseWithSafePath(const Value *Arg, BasicBlock *BB,
|
|||
dyn_cast_or_null<CallInst>(*DepInsts.begin());
|
||||
if (!Autorelease)
|
||||
return nullptr;
|
||||
InstructionClass AutoreleaseClass = GetBasicInstructionClass(Autorelease);
|
||||
ARCInstKind AutoreleaseClass = GetBasicARCInstKind(Autorelease);
|
||||
if (!IsAutorelease(AutoreleaseClass))
|
||||
return nullptr;
|
||||
if (GetArgRCIdentityRoot(Autorelease) != Arg)
|
||||
|
@ -2974,13 +2976,13 @@ ObjCARCOpt::GatherStatistics(Function &F, bool AfterOptimization) {
|
|||
|
||||
for (inst_iterator I = inst_begin(&F), E = inst_end(&F); I != E; ) {
|
||||
Instruction *Inst = &*I++;
|
||||
switch (GetBasicInstructionClass(Inst)) {
|
||||
switch (GetBasicARCInstKind(Inst)) {
|
||||
default:
|
||||
break;
|
||||
case IC_Retain:
|
||||
case ARCInstKind::Retain:
|
||||
++NumRetains;
|
||||
break;
|
||||
case IC_Release:
|
||||
case ARCInstKind::Release:
|
||||
++NumReleases;
|
||||
break;
|
||||
}
|
||||
|
@ -3052,27 +3054,27 @@ bool ObjCARCOpt::runOnFunction(Function &F) {
|
|||
OptimizeIndividualCalls(F);
|
||||
|
||||
// Optimizations for weak pointers.
|
||||
if (UsedInThisFunction & ((1 << IC_LoadWeak) |
|
||||
(1 << IC_LoadWeakRetained) |
|
||||
(1 << IC_StoreWeak) |
|
||||
(1 << IC_InitWeak) |
|
||||
(1 << IC_CopyWeak) |
|
||||
(1 << IC_MoveWeak) |
|
||||
(1 << IC_DestroyWeak)))
|
||||
if (UsedInThisFunction & ((1 << unsigned(ARCInstKind::LoadWeak)) |
|
||||
(1 << unsigned(ARCInstKind::LoadWeakRetained)) |
|
||||
(1 << unsigned(ARCInstKind::StoreWeak)) |
|
||||
(1 << unsigned(ARCInstKind::InitWeak)) |
|
||||
(1 << unsigned(ARCInstKind::CopyWeak)) |
|
||||
(1 << unsigned(ARCInstKind::MoveWeak)) |
|
||||
(1 << unsigned(ARCInstKind::DestroyWeak))))
|
||||
OptimizeWeakCalls(F);
|
||||
|
||||
// Optimizations for retain+release pairs.
|
||||
if (UsedInThisFunction & ((1 << IC_Retain) |
|
||||
(1 << IC_RetainRV) |
|
||||
(1 << IC_RetainBlock)))
|
||||
if (UsedInThisFunction & (1 << IC_Release))
|
||||
if (UsedInThisFunction & ((1 << unsigned(ARCInstKind::Retain)) |
|
||||
(1 << unsigned(ARCInstKind::RetainRV)) |
|
||||
(1 << unsigned(ARCInstKind::RetainBlock))))
|
||||
if (UsedInThisFunction & (1 << unsigned(ARCInstKind::Release)))
|
||||
// Run OptimizeSequences until it either stops making changes or
|
||||
// no retain+release pair nesting is detected.
|
||||
while (OptimizeSequences(F)) {}
|
||||
|
||||
// Optimizations if objc_autorelease is used.
|
||||
if (UsedInThisFunction & ((1 << IC_Autorelease) |
|
||||
(1 << IC_AutoreleaseRV)))
|
||||
if (UsedInThisFunction & ((1 << unsigned(ARCInstKind::Autorelease)) |
|
||||
(1 << unsigned(ARCInstKind::AutoreleaseRV))))
|
||||
OptimizeReturns(F);
|
||||
|
||||
// Gather statistics after optimization.
|
||||
|
|
|
@ -1,254 +0,0 @@
|
|||
//===- ObjCARCUtil.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 several utility functions used by various ARC
|
||||
/// optimizations which are IMHO too big to be in a header file.
|
||||
///
|
||||
/// 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 "llvm/IR/Intrinsics.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::objcarc;
|
||||
|
||||
raw_ostream &llvm::objcarc::operator<<(raw_ostream &OS,
|
||||
const InstructionClass Class) {
|
||||
switch (Class) {
|
||||
case IC_Retain:
|
||||
return OS << "IC_Retain";
|
||||
case IC_RetainRV:
|
||||
return OS << "IC_RetainRV";
|
||||
case IC_RetainBlock:
|
||||
return OS << "IC_RetainBlock";
|
||||
case IC_Release:
|
||||
return OS << "IC_Release";
|
||||
case IC_Autorelease:
|
||||
return OS << "IC_Autorelease";
|
||||
case IC_AutoreleaseRV:
|
||||
return OS << "IC_AutoreleaseRV";
|
||||
case IC_AutoreleasepoolPush:
|
||||
return OS << "IC_AutoreleasepoolPush";
|
||||
case IC_AutoreleasepoolPop:
|
||||
return OS << "IC_AutoreleasepoolPop";
|
||||
case IC_NoopCast:
|
||||
return OS << "IC_NoopCast";
|
||||
case IC_FusedRetainAutorelease:
|
||||
return OS << "IC_FusedRetainAutorelease";
|
||||
case IC_FusedRetainAutoreleaseRV:
|
||||
return OS << "IC_FusedRetainAutoreleaseRV";
|
||||
case IC_LoadWeakRetained:
|
||||
return OS << "IC_LoadWeakRetained";
|
||||
case IC_StoreWeak:
|
||||
return OS << "IC_StoreWeak";
|
||||
case IC_InitWeak:
|
||||
return OS << "IC_InitWeak";
|
||||
case IC_LoadWeak:
|
||||
return OS << "IC_LoadWeak";
|
||||
case IC_MoveWeak:
|
||||
return OS << "IC_MoveWeak";
|
||||
case IC_CopyWeak:
|
||||
return OS << "IC_CopyWeak";
|
||||
case IC_DestroyWeak:
|
||||
return OS << "IC_DestroyWeak";
|
||||
case IC_StoreStrong:
|
||||
return OS << "IC_StoreStrong";
|
||||
case IC_CallOrUser:
|
||||
return OS << "IC_CallOrUser";
|
||||
case IC_Call:
|
||||
return OS << "IC_Call";
|
||||
case IC_User:
|
||||
return OS << "IC_User";
|
||||
case IC_IntrinsicUser:
|
||||
return OS << "IC_IntrinsicUser";
|
||||
case IC_None:
|
||||
return OS << "IC_None";
|
||||
}
|
||||
llvm_unreachable("Unknown instruction class!");
|
||||
}
|
||||
|
||||
InstructionClass llvm::objcarc::GetFunctionClass(const Function *F) {
|
||||
Function::const_arg_iterator AI = F->arg_begin(), AE = F->arg_end();
|
||||
|
||||
// No (mandatory) arguments.
|
||||
if (AI == AE)
|
||||
return StringSwitch<InstructionClass>(F->getName())
|
||||
.Case("objc_autoreleasePoolPush", IC_AutoreleasepoolPush)
|
||||
.Case("clang.arc.use", IC_IntrinsicUser)
|
||||
.Default(IC_CallOrUser);
|
||||
|
||||
// One argument.
|
||||
const Argument *A0 = AI++;
|
||||
if (AI == AE)
|
||||
// Argument is a pointer.
|
||||
if (PointerType *PTy = dyn_cast<PointerType>(A0->getType())) {
|
||||
Type *ETy = PTy->getElementType();
|
||||
// Argument is i8*.
|
||||
if (ETy->isIntegerTy(8))
|
||||
return StringSwitch<InstructionClass>(F->getName())
|
||||
.Case("objc_retain", IC_Retain)
|
||||
.Case("objc_retainAutoreleasedReturnValue", IC_RetainRV)
|
||||
.Case("objc_retainBlock", IC_RetainBlock)
|
||||
.Case("objc_release", IC_Release)
|
||||
.Case("objc_autorelease", IC_Autorelease)
|
||||
.Case("objc_autoreleaseReturnValue", IC_AutoreleaseRV)
|
||||
.Case("objc_autoreleasePoolPop", IC_AutoreleasepoolPop)
|
||||
.Case("objc_retainedObject", IC_NoopCast)
|
||||
.Case("objc_unretainedObject", IC_NoopCast)
|
||||
.Case("objc_unretainedPointer", IC_NoopCast)
|
||||
.Case("objc_retain_autorelease", IC_FusedRetainAutorelease)
|
||||
.Case("objc_retainAutorelease", IC_FusedRetainAutorelease)
|
||||
.Case("objc_retainAutoreleaseReturnValue",IC_FusedRetainAutoreleaseRV)
|
||||
.Case("objc_sync_enter", IC_User)
|
||||
.Case("objc_sync_exit", IC_User)
|
||||
.Default(IC_CallOrUser);
|
||||
|
||||
// Argument is i8**
|
||||
if (PointerType *Pte = dyn_cast<PointerType>(ETy))
|
||||
if (Pte->getElementType()->isIntegerTy(8))
|
||||
return StringSwitch<InstructionClass>(F->getName())
|
||||
.Case("objc_loadWeakRetained", IC_LoadWeakRetained)
|
||||
.Case("objc_loadWeak", IC_LoadWeak)
|
||||
.Case("objc_destroyWeak", IC_DestroyWeak)
|
||||
.Default(IC_CallOrUser);
|
||||
}
|
||||
|
||||
// Two arguments, first is i8**.
|
||||
const Argument *A1 = AI++;
|
||||
if (AI == AE)
|
||||
if (PointerType *PTy = dyn_cast<PointerType>(A0->getType()))
|
||||
if (PointerType *Pte = dyn_cast<PointerType>(PTy->getElementType()))
|
||||
if (Pte->getElementType()->isIntegerTy(8))
|
||||
if (PointerType *PTy1 = dyn_cast<PointerType>(A1->getType())) {
|
||||
Type *ETy1 = PTy1->getElementType();
|
||||
// Second argument is i8*
|
||||
if (ETy1->isIntegerTy(8))
|
||||
return StringSwitch<InstructionClass>(F->getName())
|
||||
.Case("objc_storeWeak", IC_StoreWeak)
|
||||
.Case("objc_initWeak", IC_InitWeak)
|
||||
.Case("objc_storeStrong", IC_StoreStrong)
|
||||
.Default(IC_CallOrUser);
|
||||
// Second argument is i8**.
|
||||
if (PointerType *Pte1 = dyn_cast<PointerType>(ETy1))
|
||||
if (Pte1->getElementType()->isIntegerTy(8))
|
||||
return StringSwitch<InstructionClass>(F->getName())
|
||||
.Case("objc_moveWeak", IC_MoveWeak)
|
||||
.Case("objc_copyWeak", IC_CopyWeak)
|
||||
// Ignore annotation calls. This is important to stop the
|
||||
// optimizer from treating annotations as uses which would
|
||||
// make the state of the pointers they are attempting to
|
||||
// elucidate to be incorrect.
|
||||
.Case("llvm.arc.annotation.topdown.bbstart", IC_None)
|
||||
.Case("llvm.arc.annotation.topdown.bbend", IC_None)
|
||||
.Case("llvm.arc.annotation.bottomup.bbstart", IC_None)
|
||||
.Case("llvm.arc.annotation.bottomup.bbend", IC_None)
|
||||
.Default(IC_CallOrUser);
|
||||
}
|
||||
|
||||
// Anything else.
|
||||
return IC_CallOrUser;
|
||||
}
|
||||
|
||||
/// \brief Determine what kind of construct V is.
|
||||
InstructionClass
|
||||
llvm::objcarc::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
|
||||
// use of an objc pointer. Bitcasts, GEPs, Selects, PHIs transfer a pointer
|
||||
// to a subsequent use, rather than using it themselves, in this sense.
|
||||
// As a short cut, several other opcodes are known to have no pointer
|
||||
// operands of interest. And ret is never followed by a release, so it's
|
||||
// not interesting to examine.
|
||||
switch (I->getOpcode()) {
|
||||
case Instruction::Call: {
|
||||
const CallInst *CI = cast<CallInst>(I);
|
||||
// Check for calls to special functions.
|
||||
if (const Function *F = CI->getCalledFunction()) {
|
||||
InstructionClass Class = GetFunctionClass(F);
|
||||
if (Class != IC_CallOrUser)
|
||||
return Class;
|
||||
|
||||
// None of the intrinsic functions do objc_release. For intrinsics, the
|
||||
// only question is whether or not they may be users.
|
||||
switch (F->getIntrinsicID()) {
|
||||
case Intrinsic::returnaddress: case Intrinsic::frameaddress:
|
||||
case Intrinsic::stacksave: case Intrinsic::stackrestore:
|
||||
case Intrinsic::vastart: case Intrinsic::vacopy: case Intrinsic::vaend:
|
||||
case Intrinsic::objectsize: case Intrinsic::prefetch:
|
||||
case Intrinsic::stackprotector:
|
||||
case Intrinsic::eh_return_i32: case Intrinsic::eh_return_i64:
|
||||
case Intrinsic::eh_typeid_for: case Intrinsic::eh_dwarf_cfa:
|
||||
case Intrinsic::eh_sjlj_lsda: case Intrinsic::eh_sjlj_functioncontext:
|
||||
case Intrinsic::init_trampoline: case Intrinsic::adjust_trampoline:
|
||||
case Intrinsic::lifetime_start: case Intrinsic::lifetime_end:
|
||||
case Intrinsic::invariant_start: case Intrinsic::invariant_end:
|
||||
// Don't let dbg info affect our results.
|
||||
case Intrinsic::dbg_declare: case Intrinsic::dbg_value:
|
||||
// Short cut: Some intrinsics obviously don't use ObjC pointers.
|
||||
return IC_None;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return GetCallSiteClass(CI);
|
||||
}
|
||||
case Instruction::Invoke:
|
||||
return GetCallSiteClass(cast<InvokeInst>(I));
|
||||
case Instruction::BitCast:
|
||||
case Instruction::GetElementPtr:
|
||||
case Instruction::Select: case Instruction::PHI:
|
||||
case Instruction::Ret: case Instruction::Br:
|
||||
case Instruction::Switch: case Instruction::IndirectBr:
|
||||
case Instruction::Alloca: case Instruction::VAArg:
|
||||
case Instruction::Add: case Instruction::FAdd:
|
||||
case Instruction::Sub: case Instruction::FSub:
|
||||
case Instruction::Mul: case Instruction::FMul:
|
||||
case Instruction::SDiv: case Instruction::UDiv: case Instruction::FDiv:
|
||||
case Instruction::SRem: case Instruction::URem: case Instruction::FRem:
|
||||
case Instruction::Shl: case Instruction::LShr: case Instruction::AShr:
|
||||
case Instruction::And: case Instruction::Or: case Instruction::Xor:
|
||||
case Instruction::SExt: case Instruction::ZExt: case Instruction::Trunc:
|
||||
case Instruction::IntToPtr: case Instruction::FCmp:
|
||||
case Instruction::FPTrunc: case Instruction::FPExt:
|
||||
case Instruction::FPToUI: case Instruction::FPToSI:
|
||||
case Instruction::UIToFP: case Instruction::SIToFP:
|
||||
case Instruction::InsertElement: case Instruction::ExtractElement:
|
||||
case Instruction::ShuffleVector:
|
||||
case Instruction::ExtractValue:
|
||||
break;
|
||||
case Instruction::ICmp:
|
||||
// Comparing a pointer with null, or any other constant, isn't an
|
||||
// interesting use, because we don't care what the pointer points to, or
|
||||
// about the values of any other dynamic reference-counted pointers.
|
||||
if (IsPotentialRetainableObjPtr(I->getOperand(1)))
|
||||
return IC_User;
|
||||
break;
|
||||
default:
|
||||
// For anything else, check all the operands.
|
||||
// Note that this includes both operands of a Store: while the first
|
||||
// operand isn't actually being dereferenced, it is being stored to
|
||||
// memory where we can no longer track who might read it and dereference
|
||||
// it, so we have to consider it potentially used.
|
||||
for (User::const_op_iterator OI = I->op_begin(), OE = I->op_end();
|
||||
OI != OE; ++OI)
|
||||
if (IsPotentialRetainableObjPtr(*OI))
|
||||
return IC_User;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, it's totally inert for ARC purposes.
|
||||
return IC_None;
|
||||
}
|
Loading…
Reference in New Issue