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
|
ObjCARCExpand.cpp
|
||||||
ObjCARCAPElim.cpp
|
ObjCARCAPElim.cpp
|
||||||
ObjCARCAliasAnalysis.cpp
|
ObjCARCAliasAnalysis.cpp
|
||||||
ObjCARCUtil.cpp
|
ARCInstKind.cpp
|
||||||
ObjCARCContract.cpp
|
ObjCARCContract.cpp
|
||||||
DependencyAnalysis.cpp
|
DependencyAnalysis.cpp
|
||||||
ProvenanceAnalysis.cpp
|
ProvenanceAnalysis.cpp
|
||||||
|
|
|
@ -32,15 +32,14 @@ using namespace llvm::objcarc;
|
||||||
|
|
||||||
/// Test whether the given instruction can result in a reference count
|
/// Test whether the given instruction can result in a reference count
|
||||||
/// modification (positive or negative) for the pointer's object.
|
/// modification (positive or negative) for the pointer's object.
|
||||||
bool
|
bool llvm::objcarc::CanAlterRefCount(const Instruction *Inst, const Value *Ptr,
|
||||||
llvm::objcarc::CanAlterRefCount(const Instruction *Inst, const Value *Ptr,
|
|
||||||
ProvenanceAnalysis &PA,
|
ProvenanceAnalysis &PA,
|
||||||
InstructionClass Class) {
|
ARCInstKind Class) {
|
||||||
switch (Class) {
|
switch (Class) {
|
||||||
case IC_Autorelease:
|
case ARCInstKind::Autorelease:
|
||||||
case IC_AutoreleaseRV:
|
case ARCInstKind::AutoreleaseRV:
|
||||||
case IC_IntrinsicUser:
|
case ARCInstKind::IntrinsicUser:
|
||||||
case IC_User:
|
case ARCInstKind::User:
|
||||||
// These operations never directly modify a reference count.
|
// These operations never directly modify a reference count.
|
||||||
return false;
|
return false;
|
||||||
default: break;
|
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
|
/// Test whether the given instruction can "use" the given pointer's object in a
|
||||||
/// way that requires the reference count to be positive.
|
/// way that requires the reference count to be positive.
|
||||||
bool
|
bool llvm::objcarc::CanUse(const Instruction *Inst, const Value *Ptr,
|
||||||
llvm::objcarc::CanUse(const Instruction *Inst, const Value *Ptr,
|
ProvenanceAnalysis &PA, ARCInstKind Class) {
|
||||||
ProvenanceAnalysis &PA, InstructionClass Class) {
|
// ARCInstKind::Call operations (as opposed to
|
||||||
// IC_Call operations (as opposed to IC_CallOrUser) never "use" objc pointers.
|
// ARCInstKind::CallOrUser) never "use" objc pointers.
|
||||||
if (Class == IC_Call)
|
if (Class == ARCInstKind::Call)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Consider various instructions which may have pointer arguments which are
|
// Consider various instructions which may have pointer arguments which are
|
||||||
|
@ -123,11 +122,11 @@ llvm::objcarc::Depends(DependenceKind Flavor, Instruction *Inst,
|
||||||
|
|
||||||
switch (Flavor) {
|
switch (Flavor) {
|
||||||
case NeedsPositiveRetainCount: {
|
case NeedsPositiveRetainCount: {
|
||||||
InstructionClass Class = GetInstructionClass(Inst);
|
ARCInstKind Class = GetARCInstKind(Inst);
|
||||||
switch (Class) {
|
switch (Class) {
|
||||||
case IC_AutoreleasepoolPop:
|
case ARCInstKind::AutoreleasepoolPop:
|
||||||
case IC_AutoreleasepoolPush:
|
case ARCInstKind::AutoreleasepoolPush:
|
||||||
case IC_None:
|
case ARCInstKind::None:
|
||||||
return false;
|
return false;
|
||||||
default:
|
default:
|
||||||
return CanUse(Inst, Arg, PA, Class);
|
return CanUse(Inst, Arg, PA, Class);
|
||||||
|
@ -135,10 +134,10 @@ llvm::objcarc::Depends(DependenceKind Flavor, Instruction *Inst,
|
||||||
}
|
}
|
||||||
|
|
||||||
case AutoreleasePoolBoundary: {
|
case AutoreleasePoolBoundary: {
|
||||||
InstructionClass Class = GetInstructionClass(Inst);
|
ARCInstKind Class = GetARCInstKind(Inst);
|
||||||
switch (Class) {
|
switch (Class) {
|
||||||
case IC_AutoreleasepoolPop:
|
case ARCInstKind::AutoreleasepoolPop:
|
||||||
case IC_AutoreleasepoolPush:
|
case ARCInstKind::AutoreleasepoolPush:
|
||||||
// These mark the end and begin of an autorelease pool scope.
|
// These mark the end and begin of an autorelease pool scope.
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
|
@ -148,13 +147,13 @@ llvm::objcarc::Depends(DependenceKind Flavor, Instruction *Inst,
|
||||||
}
|
}
|
||||||
|
|
||||||
case CanChangeRetainCount: {
|
case CanChangeRetainCount: {
|
||||||
InstructionClass Class = GetInstructionClass(Inst);
|
ARCInstKind Class = GetARCInstKind(Inst);
|
||||||
switch (Class) {
|
switch (Class) {
|
||||||
case IC_AutoreleasepoolPop:
|
case ARCInstKind::AutoreleasepoolPop:
|
||||||
// Conservatively assume this can decrement any count.
|
// Conservatively assume this can decrement any count.
|
||||||
return true;
|
return true;
|
||||||
case IC_AutoreleasepoolPush:
|
case ARCInstKind::AutoreleasepoolPush:
|
||||||
case IC_None:
|
case ARCInstKind::None:
|
||||||
return false;
|
return false;
|
||||||
default:
|
default:
|
||||||
return CanAlterRefCount(Inst, Arg, PA, Class);
|
return CanAlterRefCount(Inst, Arg, PA, Class);
|
||||||
|
@ -162,14 +161,14 @@ llvm::objcarc::Depends(DependenceKind Flavor, Instruction *Inst,
|
||||||
}
|
}
|
||||||
|
|
||||||
case RetainAutoreleaseDep:
|
case RetainAutoreleaseDep:
|
||||||
switch (GetBasicInstructionClass(Inst)) {
|
switch (GetBasicARCInstKind(Inst)) {
|
||||||
case IC_AutoreleasepoolPop:
|
case ARCInstKind::AutoreleasepoolPop:
|
||||||
case IC_AutoreleasepoolPush:
|
case ARCInstKind::AutoreleasepoolPush:
|
||||||
// Don't merge an objc_autorelease with an objc_retain inside a different
|
// Don't merge an objc_autorelease with an objc_retain inside a different
|
||||||
// autoreleasepool scope.
|
// autoreleasepool scope.
|
||||||
return true;
|
return true;
|
||||||
case IC_Retain:
|
case ARCInstKind::Retain:
|
||||||
case IC_RetainRV:
|
case ARCInstKind::RetainRV:
|
||||||
// Check for a retain of the same pointer for merging.
|
// Check for a retain of the same pointer for merging.
|
||||||
return GetArgRCIdentityRoot(Inst) == Arg;
|
return GetArgRCIdentityRoot(Inst) == Arg;
|
||||||
default:
|
default:
|
||||||
|
@ -178,10 +177,10 @@ llvm::objcarc::Depends(DependenceKind Flavor, Instruction *Inst,
|
||||||
}
|
}
|
||||||
|
|
||||||
case RetainAutoreleaseRVDep: {
|
case RetainAutoreleaseRVDep: {
|
||||||
InstructionClass Class = GetBasicInstructionClass(Inst);
|
ARCInstKind Class = GetBasicARCInstKind(Inst);
|
||||||
switch (Class) {
|
switch (Class) {
|
||||||
case IC_Retain:
|
case ARCInstKind::Retain:
|
||||||
case IC_RetainRV:
|
case ARCInstKind::RetainRV:
|
||||||
// Check for a retain of the same pointer for merging.
|
// Check for a retain of the same pointer for merging.
|
||||||
return GetArgRCIdentityRoot(Inst) == Arg;
|
return GetArgRCIdentityRoot(Inst) == Arg;
|
||||||
default:
|
default:
|
||||||
|
@ -192,7 +191,7 @@ llvm::objcarc::Depends(DependenceKind Flavor, Instruction *Inst,
|
||||||
}
|
}
|
||||||
|
|
||||||
case RetainRVDep:
|
case RetainRVDep:
|
||||||
return CanInterruptRV(GetBasicInstructionClass(Inst));
|
return CanInterruptRV(GetBasicARCInstKind(Inst));
|
||||||
}
|
}
|
||||||
|
|
||||||
llvm_unreachable("Invalid dependence flavor");
|
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
|
/// Test whether the given instruction can "use" the given pointer's object in a
|
||||||
/// way that requires the reference count to be positive.
|
/// way that requires the reference count to be positive.
|
||||||
bool
|
bool CanUse(const Instruction *Inst, const Value *Ptr, ProvenanceAnalysis &PA,
|
||||||
CanUse(const Instruction *Inst, const Value *Ptr, ProvenanceAnalysis &PA,
|
ARCInstKind Class);
|
||||||
InstructionClass Class);
|
|
||||||
|
|
||||||
/// Test whether the given instruction can result in a reference count
|
/// Test whether the given instruction can result in a reference count
|
||||||
/// modification (positive or negative) for the pointer's object.
|
/// modification (positive or negative) for the pointer's object.
|
||||||
bool
|
bool CanAlterRefCount(const Instruction *Inst, const Value *Ptr,
|
||||||
CanAlterRefCount(const Instruction *Inst, const Value *Ptr,
|
ProvenanceAnalysis &PA, ARCInstKind Class);
|
||||||
ProvenanceAnalysis &PA, InstructionClass Class);
|
|
||||||
|
|
||||||
} // namespace objcarc
|
} // namespace objcarc
|
||||||
} // namespace llvm
|
} // namespace llvm
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
#include "llvm/Pass.h"
|
#include "llvm/Pass.h"
|
||||||
#include "llvm/Transforms/ObjCARC.h"
|
#include "llvm/Transforms/ObjCARC.h"
|
||||||
#include "llvm/Transforms/Utils/Local.h"
|
#include "llvm/Transforms/Utils/Local.h"
|
||||||
|
#include "ARCInstKind.h"
|
||||||
|
|
||||||
namespace llvm {
|
namespace llvm {
|
||||||
class raw_ostream;
|
class raw_ostream;
|
||||||
|
@ -68,160 +69,13 @@ static inline bool ModuleHasARC(const Module &M) {
|
||||||
M.getNamedValue("clang.arc.use");
|
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
|
/// \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
|
/// look through objc_retain and objc_autorelease calls, which we know to return
|
||||||
/// their argument verbatim.
|
/// their argument verbatim.
|
||||||
static inline const Value *GetUnderlyingObjCPtr(const Value *V) {
|
static inline const Value *GetUnderlyingObjCPtr(const Value *V) {
|
||||||
for (;;) {
|
for (;;) {
|
||||||
V = GetUnderlyingObject(V);
|
V = GetUnderlyingObject(V);
|
||||||
if (!IsForwarding(GetBasicInstructionClass(V)))
|
if (!IsForwarding(GetBasicARCInstKind(V)))
|
||||||
break;
|
break;
|
||||||
V = cast<CallInst>(V)->getArgOperand(0);
|
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) {
|
static inline const Value *GetRCIdentityRoot(const Value *V) {
|
||||||
for (;;) {
|
for (;;) {
|
||||||
V = V->stripPointerCasts();
|
V = V->stripPointerCasts();
|
||||||
if (!IsForwarding(GetBasicInstructionClass(V)))
|
if (!IsForwarding(GetBasicARCInstKind(V)))
|
||||||
break;
|
break;
|
||||||
V = cast<CallInst>(V)->getArgOperand(0);
|
V = cast<CallInst>(V)->getArgOperand(0);
|
||||||
}
|
}
|
||||||
|
@ -293,8 +147,8 @@ static inline void EraseInstruction(Instruction *CI) {
|
||||||
|
|
||||||
if (!Unused) {
|
if (!Unused) {
|
||||||
// Replace the return value with the argument.
|
// Replace the return value with the argument.
|
||||||
assert((IsForwarding(GetBasicInstructionClass(CI)) ||
|
assert((IsForwarding(GetBasicARCInstKind(CI)) ||
|
||||||
(IsNoopOnNull(GetBasicInstructionClass(CI)) &&
|
(IsNoopOnNull(GetBasicARCInstKind(CI)) &&
|
||||||
isa<ConstantPointerNull>(OldArg))) &&
|
isa<ConstantPointerNull>(OldArg))) &&
|
||||||
"Can't delete non-forwarding instruction with users!");
|
"Can't delete non-forwarding instruction with users!");
|
||||||
CI->replaceAllUsesWith(OldArg);
|
CI->replaceAllUsesWith(OldArg);
|
||||||
|
@ -351,15 +205,15 @@ static inline bool IsPotentialRetainableObjPtr(const Value *Op,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// \brief Helper for GetInstructionClass. Determines what kind of construct CS
|
/// \brief Helper for GetARCInstKind. Determines what kind of construct CS
|
||||||
/// is.
|
/// 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();
|
for (ImmutableCallSite::arg_iterator I = CS.arg_begin(), E = CS.arg_end();
|
||||||
I != E; ++I)
|
I != E; ++I)
|
||||||
if (IsPotentialRetainableObjPtr(*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
|
/// \brief Return true if this value refers to a distinct and identifiable
|
||||||
|
|
|
@ -97,11 +97,11 @@ bool ObjCARCAPElim::OptimizeBB(BasicBlock *BB) {
|
||||||
Instruction *Push = nullptr;
|
Instruction *Push = nullptr;
|
||||||
for (BasicBlock::iterator I = BB->begin(), E = BB->end(); I != E; ) {
|
for (BasicBlock::iterator I = BB->begin(), E = BB->end(); I != E; ) {
|
||||||
Instruction *Inst = I++;
|
Instruction *Inst = I++;
|
||||||
switch (GetBasicInstructionClass(Inst)) {
|
switch (GetBasicARCInstKind(Inst)) {
|
||||||
case IC_AutoreleasepoolPush:
|
case ARCInstKind::AutoreleasepoolPush:
|
||||||
Push = Inst;
|
Push = Inst;
|
||||||
break;
|
break;
|
||||||
case IC_AutoreleasepoolPop:
|
case ARCInstKind::AutoreleasepoolPop:
|
||||||
// If this pop matches a push and nothing in between can autorelease,
|
// If this pop matches a push and nothing in between can autorelease,
|
||||||
// zap the pair.
|
// zap the pair.
|
||||||
if (Push && cast<CallInst>(Inst)->getArgOperand(0) == Push) {
|
if (Push && cast<CallInst>(Inst)->getArgOperand(0) == Push) {
|
||||||
|
@ -115,7 +115,7 @@ bool ObjCARCAPElim::OptimizeBB(BasicBlock *BB) {
|
||||||
}
|
}
|
||||||
Push = nullptr;
|
Push = nullptr;
|
||||||
break;
|
break;
|
||||||
case IC_CallOrUser:
|
case ARCInstKind::CallOrUser:
|
||||||
if (MayAutorelease(ImmutableCallSite(Inst)))
|
if (MayAutorelease(ImmutableCallSite(Inst)))
|
||||||
Push = nullptr;
|
Push = nullptr;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -120,7 +120,7 @@ ObjCARCAliasAnalysis::getModRefBehavior(const Function *F) {
|
||||||
return AliasAnalysis::getModRefBehavior(F);
|
return AliasAnalysis::getModRefBehavior(F);
|
||||||
|
|
||||||
switch (GetFunctionClass(F)) {
|
switch (GetFunctionClass(F)) {
|
||||||
case IC_NoopCast:
|
case ARCInstKind::NoopCast:
|
||||||
return DoesNotAccessMemory;
|
return DoesNotAccessMemory;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
@ -134,15 +134,15 @@ ObjCARCAliasAnalysis::getModRefInfo(ImmutableCallSite CS, const Location &Loc) {
|
||||||
if (!EnableARCOpts)
|
if (!EnableARCOpts)
|
||||||
return AliasAnalysis::getModRefInfo(CS, Loc);
|
return AliasAnalysis::getModRefInfo(CS, Loc);
|
||||||
|
|
||||||
switch (GetBasicInstructionClass(CS.getInstruction())) {
|
switch (GetBasicARCInstKind(CS.getInstruction())) {
|
||||||
case IC_Retain:
|
case ARCInstKind::Retain:
|
||||||
case IC_RetainRV:
|
case ARCInstKind::RetainRV:
|
||||||
case IC_Autorelease:
|
case ARCInstKind::Autorelease:
|
||||||
case IC_AutoreleaseRV:
|
case ARCInstKind::AutoreleaseRV:
|
||||||
case IC_NoopCast:
|
case ARCInstKind::NoopCast:
|
||||||
case IC_AutoreleasepoolPush:
|
case ARCInstKind::AutoreleasepoolPush:
|
||||||
case IC_FusedRetainAutorelease:
|
case ARCInstKind::FusedRetainAutorelease:
|
||||||
case IC_FusedRetainAutoreleaseRV:
|
case ARCInstKind::FusedRetainAutoreleaseRV:
|
||||||
// These functions don't access any memory visible to the compiler.
|
// These functions don't access any memory visible to the compiler.
|
||||||
// Note that this doesn't include objc_retainBlock, because it updates
|
// Note that this doesn't include objc_retainBlock, because it updates
|
||||||
// pointers when it copies block data.
|
// pointers when it copies block data.
|
||||||
|
|
|
@ -83,7 +83,7 @@ namespace {
|
||||||
|
|
||||||
bool
|
bool
|
||||||
contractAutorelease(Function &F, Instruction *Autorelease,
|
contractAutorelease(Function &F, Instruction *Autorelease,
|
||||||
InstructionClass Class,
|
ARCInstKind Class,
|
||||||
SmallPtrSetImpl<Instruction *> &DependingInstructions,
|
SmallPtrSetImpl<Instruction *> &DependingInstructions,
|
||||||
SmallPtrSetImpl<const BasicBlock *> &Visited);
|
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.
|
/// Merge an autorelease with a retain into a fused call.
|
||||||
bool ObjCARCContract::contractAutorelease(
|
bool ObjCARCContract::contractAutorelease(
|
||||||
Function &F, Instruction *Autorelease, InstructionClass Class,
|
Function &F, Instruction *Autorelease, ARCInstKind Class,
|
||||||
SmallPtrSetImpl<Instruction *> &DependingInstructions,
|
SmallPtrSetImpl<Instruction *> &DependingInstructions,
|
||||||
SmallPtrSetImpl<const BasicBlock *> &Visited) {
|
SmallPtrSetImpl<const BasicBlock *> &Visited) {
|
||||||
const Value *Arg = GetArgRCIdentityRoot(Autorelease);
|
const Value *Arg = GetArgRCIdentityRoot(Autorelease);
|
||||||
|
@ -151,7 +151,7 @@ bool ObjCARCContract::contractAutorelease(
|
||||||
// Check that there are no instructions between the retain and the autorelease
|
// Check that there are no instructions between the retain and the autorelease
|
||||||
// (such as an autorelease_pop) which may change the count.
|
// (such as an autorelease_pop) which may change the count.
|
||||||
CallInst *Retain = nullptr;
|
CallInst *Retain = nullptr;
|
||||||
if (Class == IC_AutoreleaseRV)
|
if (Class == ARCInstKind::AutoreleaseRV)
|
||||||
FindDependencies(RetainAutoreleaseRVDep, Arg,
|
FindDependencies(RetainAutoreleaseRVDep, Arg,
|
||||||
Autorelease->getParent(), Autorelease,
|
Autorelease->getParent(), Autorelease,
|
||||||
DependingInstructions, Visited, PA);
|
DependingInstructions, Visited, PA);
|
||||||
|
@ -169,8 +169,7 @@ bool ObjCARCContract::contractAutorelease(
|
||||||
Retain = dyn_cast_or_null<CallInst>(*DependingInstructions.begin());
|
Retain = dyn_cast_or_null<CallInst>(*DependingInstructions.begin());
|
||||||
DependingInstructions.clear();
|
DependingInstructions.clear();
|
||||||
|
|
||||||
if (!Retain ||
|
if (!Retain || GetBasicARCInstKind(Retain) != ARCInstKind::Retain ||
|
||||||
GetBasicInstructionClass(Retain) != IC_Retain ||
|
|
||||||
GetArgRCIdentityRoot(Retain) != Arg)
|
GetArgRCIdentityRoot(Retain) != Arg)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -181,9 +180,9 @@ bool ObjCARCContract::contractAutorelease(
|
||||||
" Autorelease:" << *Autorelease << "\n"
|
" Autorelease:" << *Autorelease << "\n"
|
||||||
" Retain: " << *Retain << "\n");
|
" Retain: " << *Retain << "\n");
|
||||||
|
|
||||||
Constant *Decl = EP.get(Class == IC_AutoreleaseRV ?
|
Constant *Decl = EP.get(Class == ARCInstKind::AutoreleaseRV
|
||||||
ARCRuntimeEntryPoints::EPT_RetainAutoreleaseRV :
|
? ARCRuntimeEntryPoints::EPT_RetainAutoreleaseRV
|
||||||
ARCRuntimeEntryPoints::EPT_RetainAutorelease);
|
: ARCRuntimeEntryPoints::EPT_RetainAutorelease);
|
||||||
Retain->setCalledFunction(Decl);
|
Retain->setCalledFunction(Decl);
|
||||||
|
|
||||||
DEBUG(dbgs() << " New RetainAutorelease: " << *Retain << "\n");
|
DEBUG(dbgs() << " New RetainAutorelease: " << *Retain << "\n");
|
||||||
|
@ -222,7 +221,7 @@ tryToContractReleaseIntoStoreStrong(Instruction *Release, inst_iterator &Iter) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
InstructionClass Class = GetBasicInstructionClass(Inst);
|
ARCInstKind Class = GetBasicARCInstKind(Inst);
|
||||||
|
|
||||||
// Unrelated retains are harmless.
|
// Unrelated retains are harmless.
|
||||||
if (IsRetain(Class))
|
if (IsRetain(Class))
|
||||||
|
@ -247,10 +246,11 @@ tryToContractReleaseIntoStoreStrong(Instruction *Release, inst_iterator &Iter) {
|
||||||
// Walk up to find the retain.
|
// Walk up to find the retain.
|
||||||
I = Store;
|
I = Store;
|
||||||
BasicBlock::iterator Begin = BB->begin();
|
BasicBlock::iterator Begin = BB->begin();
|
||||||
while (I != Begin && GetBasicInstructionClass(I) != IC_Retain)
|
while (I != Begin && GetBasicARCInstKind(I) != ARCInstKind::Retain)
|
||||||
--I;
|
--I;
|
||||||
Instruction *Retain = I;
|
Instruction *Retain = I;
|
||||||
if (GetBasicInstructionClass(Retain) != IC_Retain) return;
|
if (GetBasicARCInstKind(Retain) != ARCInstKind::Retain)
|
||||||
|
return;
|
||||||
if (GetArgRCIdentityRoot(Retain) != New) return;
|
if (GetArgRCIdentityRoot(Retain) != New) return;
|
||||||
|
|
||||||
Changed = true;
|
Changed = true;
|
||||||
|
@ -300,22 +300,22 @@ bool ObjCARCContract::tryToPeepholeInstruction(
|
||||||
bool &TailOkForStoreStrongs) {
|
bool &TailOkForStoreStrongs) {
|
||||||
// Only these library routines return their argument. In particular,
|
// Only these library routines return their argument. In particular,
|
||||||
// objc_retainBlock does not necessarily return its argument.
|
// objc_retainBlock does not necessarily return its argument.
|
||||||
InstructionClass Class = GetBasicInstructionClass(Inst);
|
ARCInstKind Class = GetBasicARCInstKind(Inst);
|
||||||
switch (Class) {
|
switch (Class) {
|
||||||
case IC_FusedRetainAutorelease:
|
case ARCInstKind::FusedRetainAutorelease:
|
||||||
case IC_FusedRetainAutoreleaseRV:
|
case ARCInstKind::FusedRetainAutoreleaseRV:
|
||||||
return false;
|
return false;
|
||||||
case IC_Autorelease:
|
case ARCInstKind::Autorelease:
|
||||||
case IC_AutoreleaseRV:
|
case ARCInstKind::AutoreleaseRV:
|
||||||
return contractAutorelease(F, Inst, Class, DependingInsts, Visited);
|
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
|
// Attempt to convert retains to retainrvs if they are next to function
|
||||||
// calls.
|
// calls.
|
||||||
if (!optimizeRetainCall(F, Inst))
|
if (!optimizeRetainCall(F, Inst))
|
||||||
return false;
|
return false;
|
||||||
// If we succeed in our optimization, fall through.
|
// If we succeed in our optimization, fall through.
|
||||||
// FALLTHROUGH
|
// FALLTHROUGH
|
||||||
case IC_RetainRV: {
|
case ARCInstKind::RetainRV: {
|
||||||
// If we're compiling for a target which needs a special inline-asm
|
// If we're compiling for a target which needs a special inline-asm
|
||||||
// marker to do the retainAutoreleasedReturnValue optimization,
|
// marker to do the retainAutoreleasedReturnValue optimization,
|
||||||
// insert it now.
|
// insert it now.
|
||||||
|
@ -352,7 +352,7 @@ bool ObjCARCContract::tryToPeepholeInstruction(
|
||||||
decline_rv_optimization:
|
decline_rv_optimization:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
case IC_InitWeak: {
|
case ARCInstKind::InitWeak: {
|
||||||
// objc_initWeak(p, null) => *p = null
|
// objc_initWeak(p, null) => *p = null
|
||||||
CallInst *CI = cast<CallInst>(Inst);
|
CallInst *CI = cast<CallInst>(Inst);
|
||||||
if (IsNullOrUndef(CI->getArgOperand(1))) {
|
if (IsNullOrUndef(CI->getArgOperand(1))) {
|
||||||
|
@ -369,19 +369,19 @@ bool ObjCARCContract::tryToPeepholeInstruction(
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case IC_Release:
|
case ARCInstKind::Release:
|
||||||
// Try to form an objc store strong from our release. If we fail, there is
|
// Try to form an objc store strong from our release. If we fail, there is
|
||||||
// nothing further to do below, so continue.
|
// nothing further to do below, so continue.
|
||||||
tryToContractReleaseIntoStoreStrong(Inst, Iter);
|
tryToContractReleaseIntoStoreStrong(Inst, Iter);
|
||||||
return true;
|
return true;
|
||||||
case IC_User:
|
case ARCInstKind::User:
|
||||||
// Be conservative if the function has any alloca instructions.
|
// Be conservative if the function has any alloca instructions.
|
||||||
// Technically we only care about escaping alloca instructions,
|
// Technically we only care about escaping alloca instructions,
|
||||||
// but this is sufficient to handle some interesting cases.
|
// but this is sufficient to handle some interesting cases.
|
||||||
if (isa<AllocaInst>(Inst))
|
if (isa<AllocaInst>(Inst))
|
||||||
TailOkForStoreStrongs = false;
|
TailOkForStoreStrongs = false;
|
||||||
return true;
|
return true;
|
||||||
case IC_IntrinsicUser:
|
case ARCInstKind::IntrinsicUser:
|
||||||
// Remove calls to @clang.arc.use(...).
|
// Remove calls to @clang.arc.use(...).
|
||||||
Inst->eraseFromParent();
|
Inst->eraseFromParent();
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -99,13 +99,13 @@ bool ObjCARCExpand::runOnFunction(Function &F) {
|
||||||
|
|
||||||
DEBUG(dbgs() << "ObjCARCExpand: Visiting: " << *Inst << "\n");
|
DEBUG(dbgs() << "ObjCARCExpand: Visiting: " << *Inst << "\n");
|
||||||
|
|
||||||
switch (GetBasicInstructionClass(Inst)) {
|
switch (GetBasicARCInstKind(Inst)) {
|
||||||
case IC_Retain:
|
case ARCInstKind::Retain:
|
||||||
case IC_RetainRV:
|
case ARCInstKind::RetainRV:
|
||||||
case IC_Autorelease:
|
case ARCInstKind::Autorelease:
|
||||||
case IC_AutoreleaseRV:
|
case ARCInstKind::AutoreleaseRV:
|
||||||
case IC_FusedRetainAutorelease:
|
case ARCInstKind::FusedRetainAutorelease:
|
||||||
case IC_FusedRetainAutoreleaseRV: {
|
case ARCInstKind::FusedRetainAutoreleaseRV: {
|
||||||
// These calls return their argument verbatim, as a low-level
|
// These calls return their argument verbatim, as a low-level
|
||||||
// optimization. However, this makes high-level optimizations
|
// optimization. However, this makes high-level optimizations
|
||||||
// harder. Undo any uses of this optimization that the front-end
|
// 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 (const GetElementPtrInst *GEP = dyn_cast<GetElementPtrInst>(Arg))
|
||||||
if (GEP->hasAllZeroIndices())
|
if (GEP->hasAllZeroIndices())
|
||||||
return FindSingleUseIdentifiedObject(GEP->getPointerOperand());
|
return FindSingleUseIdentifiedObject(GEP->getPointerOperand());
|
||||||
if (IsForwarding(GetBasicInstructionClass(Arg)))
|
if (IsForwarding(GetBasicARCInstKind(Arg)))
|
||||||
return FindSingleUseIdentifiedObject(
|
return FindSingleUseIdentifiedObject(
|
||||||
cast<CallInst>(Arg)->getArgOperand(0));
|
cast<CallInst>(Arg)->getArgOperand(0));
|
||||||
if (!IsObjCIdentifiedObject(Arg))
|
if (!IsObjCIdentifiedObject(Arg))
|
||||||
|
@ -1096,7 +1096,7 @@ namespace {
|
||||||
|
|
||||||
bool OptimizeRetainRVCall(Function &F, Instruction *RetainRV);
|
bool OptimizeRetainRVCall(Function &F, Instruction *RetainRV);
|
||||||
void OptimizeAutoreleaseRVCall(Function &F, Instruction *AutoreleaseRV,
|
void OptimizeAutoreleaseRVCall(Function &F, Instruction *AutoreleaseRV,
|
||||||
InstructionClass &Class);
|
ARCInstKind &Class);
|
||||||
void OptimizeIndividualCalls(Function &F);
|
void OptimizeIndividualCalls(Function &F);
|
||||||
|
|
||||||
void CheckForCFGHazards(const BasicBlock *BB,
|
void CheckForCFGHazards(const BasicBlock *BB,
|
||||||
|
@ -1216,7 +1216,7 @@ ObjCARCOpt::OptimizeRetainRVCall(Function &F, Instruction *RetainRV) {
|
||||||
BasicBlock::iterator I = RetainRV, Begin = RetainRV->getParent()->begin();
|
BasicBlock::iterator I = RetainRV, Begin = RetainRV->getParent()->begin();
|
||||||
if (I != Begin) {
|
if (I != Begin) {
|
||||||
do --I; while (I != Begin && IsNoopInstruction(I));
|
do --I; while (I != Begin && IsNoopInstruction(I));
|
||||||
if (GetBasicInstructionClass(I) == IC_AutoreleaseRV &&
|
if (GetBasicARCInstKind(I) == ARCInstKind::AutoreleaseRV &&
|
||||||
GetArgRCIdentityRoot(I) == Arg) {
|
GetArgRCIdentityRoot(I) == Arg) {
|
||||||
Changed = true;
|
Changed = true;
|
||||||
++NumPeeps;
|
++NumPeeps;
|
||||||
|
@ -1248,9 +1248,9 @@ ObjCARCOpt::OptimizeRetainRVCall(Function &F, Instruction *RetainRV) {
|
||||||
|
|
||||||
/// Turn objc_autoreleaseReturnValue into objc_autorelease if the result is not
|
/// Turn objc_autoreleaseReturnValue into objc_autorelease if the result is not
|
||||||
/// used as a return value.
|
/// used as a return value.
|
||||||
void
|
void ObjCARCOpt::OptimizeAutoreleaseRVCall(Function &F,
|
||||||
ObjCARCOpt::OptimizeAutoreleaseRVCall(Function &F, Instruction *AutoreleaseRV,
|
Instruction *AutoreleaseRV,
|
||||||
InstructionClass &Class) {
|
ARCInstKind &Class) {
|
||||||
// Check for a return of the pointer value.
|
// Check for a return of the pointer value.
|
||||||
const Value *Ptr = GetArgRCIdentityRoot(AutoreleaseRV);
|
const Value *Ptr = GetArgRCIdentityRoot(AutoreleaseRV);
|
||||||
SmallVector<const Value *, 2> Users;
|
SmallVector<const Value *, 2> Users;
|
||||||
|
@ -1258,7 +1258,7 @@ ObjCARCOpt::OptimizeAutoreleaseRVCall(Function &F, Instruction *AutoreleaseRV,
|
||||||
do {
|
do {
|
||||||
Ptr = Users.pop_back_val();
|
Ptr = Users.pop_back_val();
|
||||||
for (const User *U : Ptr->users()) {
|
for (const User *U : Ptr->users()) {
|
||||||
if (isa<ReturnInst>(U) || GetBasicInstructionClass(U) == IC_RetainRV)
|
if (isa<ReturnInst>(U) || GetBasicARCInstKind(U) == ARCInstKind::RetainRV)
|
||||||
return;
|
return;
|
||||||
if (isa<BitCastInst>(U))
|
if (isa<BitCastInst>(U))
|
||||||
Users.push_back(U);
|
Users.push_back(U);
|
||||||
|
@ -1277,7 +1277,7 @@ ObjCARCOpt::OptimizeAutoreleaseRVCall(Function &F, Instruction *AutoreleaseRV,
|
||||||
Constant *NewDecl = EP.get(ARCRuntimeEntryPoints::EPT_Autorelease);
|
Constant *NewDecl = EP.get(ARCRuntimeEntryPoints::EPT_Autorelease);
|
||||||
AutoreleaseRVCI->setCalledFunction(NewDecl);
|
AutoreleaseRVCI->setCalledFunction(NewDecl);
|
||||||
AutoreleaseRVCI->setTailCall(false); // Never tail call objc_autorelease.
|
AutoreleaseRVCI->setTailCall(false); // Never tail call objc_autorelease.
|
||||||
Class = IC_Autorelease;
|
Class = ARCInstKind::Autorelease;
|
||||||
|
|
||||||
DEBUG(dbgs() << "New: " << *AutoreleaseRV << "\n");
|
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; ) {
|
for (inst_iterator I = inst_begin(&F), E = inst_end(&F); I != E; ) {
|
||||||
Instruction *Inst = &*I++;
|
Instruction *Inst = &*I++;
|
||||||
|
|
||||||
InstructionClass Class = GetBasicInstructionClass(Inst);
|
ARCInstKind Class = GetBasicARCInstKind(Inst);
|
||||||
|
|
||||||
DEBUG(dbgs() << "Visiting: Class: " << Class << "; " << *Inst << "\n");
|
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
|
// There are gray areas here, as the ability to cast reference-counted
|
||||||
// pointers to raw void* and back allows code to break ARC assumptions,
|
// pointers to raw void* and back allows code to break ARC assumptions,
|
||||||
// however these are currently considered to be unimportant.
|
// however these are currently considered to be unimportant.
|
||||||
case IC_NoopCast:
|
case ARCInstKind::NoopCast:
|
||||||
Changed = true;
|
Changed = true;
|
||||||
++NumNoops;
|
++NumNoops;
|
||||||
DEBUG(dbgs() << "Erasing no-op cast: " << *Inst << "\n");
|
DEBUG(dbgs() << "Erasing no-op cast: " << *Inst << "\n");
|
||||||
|
@ -1317,11 +1317,11 @@ void ObjCARCOpt::OptimizeIndividualCalls(Function &F) {
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// If the pointer-to-weak-pointer is null, it's undefined behavior.
|
// If the pointer-to-weak-pointer is null, it's undefined behavior.
|
||||||
case IC_StoreWeak:
|
case ARCInstKind::StoreWeak:
|
||||||
case IC_LoadWeak:
|
case ARCInstKind::LoadWeak:
|
||||||
case IC_LoadWeakRetained:
|
case ARCInstKind::LoadWeakRetained:
|
||||||
case IC_InitWeak:
|
case ARCInstKind::InitWeak:
|
||||||
case IC_DestroyWeak: {
|
case ARCInstKind::DestroyWeak: {
|
||||||
CallInst *CI = cast<CallInst>(Inst);
|
CallInst *CI = cast<CallInst>(Inst);
|
||||||
if (IsNullOrUndef(CI->getArgOperand(0))) {
|
if (IsNullOrUndef(CI->getArgOperand(0))) {
|
||||||
Changed = true;
|
Changed = true;
|
||||||
|
@ -1338,8 +1338,8 @@ void ObjCARCOpt::OptimizeIndividualCalls(Function &F) {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case IC_CopyWeak:
|
case ARCInstKind::CopyWeak:
|
||||||
case IC_MoveWeak: {
|
case ARCInstKind::MoveWeak: {
|
||||||
CallInst *CI = cast<CallInst>(Inst);
|
CallInst *CI = cast<CallInst>(Inst);
|
||||||
if (IsNullOrUndef(CI->getArgOperand(0)) ||
|
if (IsNullOrUndef(CI->getArgOperand(0)) ||
|
||||||
IsNullOrUndef(CI->getArgOperand(1))) {
|
IsNullOrUndef(CI->getArgOperand(1))) {
|
||||||
|
@ -1359,11 +1359,11 @@ void ObjCARCOpt::OptimizeIndividualCalls(Function &F) {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case IC_RetainRV:
|
case ARCInstKind::RetainRV:
|
||||||
if (OptimizeRetainRVCall(F, Inst))
|
if (OptimizeRetainRVCall(F, Inst))
|
||||||
continue;
|
continue;
|
||||||
break;
|
break;
|
||||||
case IC_AutoreleaseRV:
|
case ARCInstKind::AutoreleaseRV:
|
||||||
OptimizeAutoreleaseRVCall(F, Inst, Class);
|
OptimizeAutoreleaseRVCall(F, Inst, Class);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1391,7 +1391,7 @@ void ObjCARCOpt::OptimizeIndividualCalls(Function &F) {
|
||||||
|
|
||||||
EraseInstruction(Call);
|
EraseInstruction(Call);
|
||||||
Inst = NewCall;
|
Inst = NewCall;
|
||||||
Class = IC_Release;
|
Class = ARCInstKind::Release;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1422,7 +1422,7 @@ void ObjCARCOpt::OptimizeIndividualCalls(Function &F) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!IsNoopOnNull(Class)) {
|
if (!IsNoopOnNull(Class)) {
|
||||||
UsedInThisFunction |= 1 << Class;
|
UsedInThisFunction |= 1 << unsigned(Class);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1440,7 +1440,7 @@ void ObjCARCOpt::OptimizeIndividualCalls(Function &F) {
|
||||||
|
|
||||||
// Keep track of which of retain, release, autorelease, and retain_block
|
// Keep track of which of retain, release, autorelease, and retain_block
|
||||||
// are actually present in this function.
|
// 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
|
// 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
|
// 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
|
// Check that there is nothing that cares about the reference
|
||||||
// count between the call and the phi.
|
// count between the call and the phi.
|
||||||
switch (Class) {
|
switch (Class) {
|
||||||
case IC_Retain:
|
case ARCInstKind::Retain:
|
||||||
case IC_RetainBlock:
|
case ARCInstKind::RetainBlock:
|
||||||
// These can always be moved up.
|
// These can always be moved up.
|
||||||
break;
|
break;
|
||||||
case IC_Release:
|
case ARCInstKind::Release:
|
||||||
// These can't be moved across things that care about the retain
|
// These can't be moved across things that care about the retain
|
||||||
// count.
|
// count.
|
||||||
FindDependencies(NeedsPositiveRetainCount, Arg,
|
FindDependencies(NeedsPositiveRetainCount, Arg,
|
||||||
Inst->getParent(), Inst,
|
Inst->getParent(), Inst,
|
||||||
DependingInstructions, Visited, PA);
|
DependingInstructions, Visited, PA);
|
||||||
break;
|
break;
|
||||||
case IC_Autorelease:
|
case ARCInstKind::Autorelease:
|
||||||
// These can't be moved across autorelease pool scope boundaries.
|
// These can't be moved across autorelease pool scope boundaries.
|
||||||
FindDependencies(AutoreleasePoolBoundary, Arg,
|
FindDependencies(AutoreleasePoolBoundary, Arg,
|
||||||
Inst->getParent(), Inst,
|
Inst->getParent(), Inst,
|
||||||
DependingInstructions, Visited, PA);
|
DependingInstructions, Visited, PA);
|
||||||
break;
|
break;
|
||||||
case IC_RetainRV:
|
case ARCInstKind::RetainRV:
|
||||||
case IC_AutoreleaseRV:
|
case ARCInstKind::AutoreleaseRV:
|
||||||
// Don't move these; the RV optimization depends on the autoreleaseRV
|
// Don't move these; the RV optimization depends on the autoreleaseRV
|
||||||
// being tail called, and the retainRV being immediately after a call
|
// being tail called, and the retainRV being immediately after a call
|
||||||
// (which might still happen if we get lucky with codegen layout, but
|
// (which might still happen if we get lucky with codegen layout, but
|
||||||
|
@ -1711,13 +1711,13 @@ ObjCARCOpt::VisitInstructionBottomUp(Instruction *Inst,
|
||||||
MapVector<Value *, RRInfo> &Retains,
|
MapVector<Value *, RRInfo> &Retains,
|
||||||
BBState &MyStates) {
|
BBState &MyStates) {
|
||||||
bool NestingDetected = false;
|
bool NestingDetected = false;
|
||||||
InstructionClass Class = GetInstructionClass(Inst);
|
ARCInstKind Class = GetARCInstKind(Inst);
|
||||||
const Value *Arg = nullptr;
|
const Value *Arg = nullptr;
|
||||||
|
|
||||||
DEBUG(dbgs() << "Class: " << Class << "\n");
|
DEBUG(dbgs() << "Class: " << Class << "\n");
|
||||||
|
|
||||||
switch (Class) {
|
switch (Class) {
|
||||||
case IC_Release: {
|
case ARCInstKind::Release: {
|
||||||
Arg = GetArgRCIdentityRoot(Inst);
|
Arg = GetArgRCIdentityRoot(Inst);
|
||||||
|
|
||||||
PtrState &S = MyStates.getPtrBottomUpState(Arg);
|
PtrState &S = MyStates.getPtrBottomUpState(Arg);
|
||||||
|
@ -1745,13 +1745,13 @@ ObjCARCOpt::VisitInstructionBottomUp(Instruction *Inst,
|
||||||
S.SetKnownPositiveRefCount();
|
S.SetKnownPositiveRefCount();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case IC_RetainBlock:
|
case ARCInstKind::RetainBlock:
|
||||||
// In OptimizeIndividualCalls, we have strength reduced all optimizable
|
// In OptimizeIndividualCalls, we have strength reduced all optimizable
|
||||||
// objc_retainBlocks to objc_retains. Thus at this point any
|
// objc_retainBlocks to objc_retains. Thus at this point any
|
||||||
// objc_retainBlocks that we see are not optimizable.
|
// objc_retainBlocks that we see are not optimizable.
|
||||||
break;
|
break;
|
||||||
case IC_Retain:
|
case ARCInstKind::Retain:
|
||||||
case IC_RetainRV: {
|
case ARCInstKind::RetainRV: {
|
||||||
Arg = GetArgRCIdentityRoot(Inst);
|
Arg = GetArgRCIdentityRoot(Inst);
|
||||||
|
|
||||||
PtrState &S = MyStates.getPtrBottomUpState(Arg);
|
PtrState &S = MyStates.getPtrBottomUpState(Arg);
|
||||||
|
@ -1769,9 +1769,10 @@ ObjCARCOpt::VisitInstructionBottomUp(Instruction *Inst,
|
||||||
S.ClearReverseInsertPts();
|
S.ClearReverseInsertPts();
|
||||||
// FALL THROUGH
|
// FALL THROUGH
|
||||||
case S_CanRelease:
|
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.
|
// better to let it remain as the first instruction after a call.
|
||||||
if (Class != IC_RetainRV)
|
if (Class != ARCInstKind::RetainRV)
|
||||||
Retains[Inst] = S.GetRRInfo();
|
Retains[Inst] = S.GetRRInfo();
|
||||||
S.ClearSequenceProgress();
|
S.ClearSequenceProgress();
|
||||||
break;
|
break;
|
||||||
|
@ -1784,15 +1785,15 @@ ObjCARCOpt::VisitInstructionBottomUp(Instruction *Inst,
|
||||||
// A retain moving bottom up can be a use.
|
// A retain moving bottom up can be a use.
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case IC_AutoreleasepoolPop:
|
case ARCInstKind::AutoreleasepoolPop:
|
||||||
// Conservatively, clear MyStates for all known pointers.
|
// Conservatively, clear MyStates for all known pointers.
|
||||||
MyStates.clearBottomUpPointers();
|
MyStates.clearBottomUpPointers();
|
||||||
return NestingDetected;
|
return NestingDetected;
|
||||||
case IC_AutoreleasepoolPush:
|
case ARCInstKind::AutoreleasepoolPush:
|
||||||
case IC_None:
|
case ARCInstKind::None:
|
||||||
// These are irrelevant.
|
// These are irrelevant.
|
||||||
return NestingDetected;
|
return NestingDetected;
|
||||||
case IC_User:
|
case ARCInstKind::User:
|
||||||
// If we have a store into an alloca of a pointer we are tracking, the
|
// 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.
|
// pointer has multiple owners implying that we must be more conservative.
|
||||||
//
|
//
|
||||||
|
@ -1967,24 +1968,25 @@ ObjCARCOpt::VisitInstructionTopDown(Instruction *Inst,
|
||||||
DenseMap<Value *, RRInfo> &Releases,
|
DenseMap<Value *, RRInfo> &Releases,
|
||||||
BBState &MyStates) {
|
BBState &MyStates) {
|
||||||
bool NestingDetected = false;
|
bool NestingDetected = false;
|
||||||
InstructionClass Class = GetInstructionClass(Inst);
|
ARCInstKind Class = GetARCInstKind(Inst);
|
||||||
const Value *Arg = nullptr;
|
const Value *Arg = nullptr;
|
||||||
|
|
||||||
switch (Class) {
|
switch (Class) {
|
||||||
case IC_RetainBlock:
|
case ARCInstKind::RetainBlock:
|
||||||
// In OptimizeIndividualCalls, we have strength reduced all optimizable
|
// In OptimizeIndividualCalls, we have strength reduced all optimizable
|
||||||
// objc_retainBlocks to objc_retains. Thus at this point any
|
// objc_retainBlocks to objc_retains. Thus at this point any
|
||||||
// objc_retainBlocks that we see are not optimizable.
|
// objc_retainBlocks that we see are not optimizable.
|
||||||
break;
|
break;
|
||||||
case IC_Retain:
|
case ARCInstKind::Retain:
|
||||||
case IC_RetainRV: {
|
case ARCInstKind::RetainRV: {
|
||||||
Arg = GetArgRCIdentityRoot(Inst);
|
Arg = GetArgRCIdentityRoot(Inst);
|
||||||
|
|
||||||
PtrState &S = MyStates.getPtrTopDownState(Arg);
|
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.
|
// 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
|
// 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
|
// a note, and we'll cicle back to revisit it after we've
|
||||||
// hopefully eliminated the second retain, which may allow us to
|
// hopefully eliminated the second retain, which may allow us to
|
||||||
|
@ -2007,7 +2009,7 @@ ObjCARCOpt::VisitInstructionTopDown(Instruction *Inst,
|
||||||
// code below.
|
// code below.
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case IC_Release: {
|
case ARCInstKind::Release: {
|
||||||
Arg = GetArgRCIdentityRoot(Inst);
|
Arg = GetArgRCIdentityRoot(Inst);
|
||||||
|
|
||||||
PtrState &S = MyStates.getPtrTopDownState(Arg);
|
PtrState &S = MyStates.getPtrTopDownState(Arg);
|
||||||
|
@ -2039,12 +2041,12 @@ ObjCARCOpt::VisitInstructionTopDown(Instruction *Inst,
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case IC_AutoreleasepoolPop:
|
case ARCInstKind::AutoreleasepoolPop:
|
||||||
// Conservatively, clear MyStates for all known pointers.
|
// Conservatively, clear MyStates for all known pointers.
|
||||||
MyStates.clearTopDownPointers();
|
MyStates.clearTopDownPointers();
|
||||||
return NestingDetected;
|
return NestingDetected;
|
||||||
case IC_AutoreleasepoolPush:
|
case ARCInstKind::AutoreleasepoolPush:
|
||||||
case IC_None:
|
case ARCInstKind::None:
|
||||||
// These are irrelevant.
|
// These are irrelevant.
|
||||||
return NestingDetected;
|
return NestingDetected;
|
||||||
default:
|
default:
|
||||||
|
@ -2640,12 +2642,13 @@ void ObjCARCOpt::OptimizeWeakCalls(Function &F) {
|
||||||
|
|
||||||
DEBUG(dbgs() << "Visiting: " << *Inst << "\n");
|
DEBUG(dbgs() << "Visiting: " << *Inst << "\n");
|
||||||
|
|
||||||
InstructionClass Class = GetBasicInstructionClass(Inst);
|
ARCInstKind Class = GetBasicARCInstKind(Inst);
|
||||||
if (Class != IC_LoadWeak && Class != IC_LoadWeakRetained)
|
if (Class != ARCInstKind::LoadWeak &&
|
||||||
|
Class != ARCInstKind::LoadWeakRetained)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Delete objc_loadWeak calls with no users.
|
// Delete objc_loadWeak calls with no users.
|
||||||
if (Class == IC_LoadWeak && Inst->use_empty()) {
|
if (Class == ARCInstKind::LoadWeak && Inst->use_empty()) {
|
||||||
Inst->eraseFromParent();
|
Inst->eraseFromParent();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -2660,10 +2663,10 @@ void ObjCARCOpt::OptimizeWeakCalls(Function &F) {
|
||||||
J = Current.getInstructionIterator();
|
J = Current.getInstructionIterator();
|
||||||
J != B; --J) {
|
J != B; --J) {
|
||||||
Instruction *EarlierInst = &*std::prev(J);
|
Instruction *EarlierInst = &*std::prev(J);
|
||||||
InstructionClass EarlierClass = GetInstructionClass(EarlierInst);
|
ARCInstKind EarlierClass = GetARCInstKind(EarlierInst);
|
||||||
switch (EarlierClass) {
|
switch (EarlierClass) {
|
||||||
case IC_LoadWeak:
|
case ARCInstKind::LoadWeak:
|
||||||
case IC_LoadWeakRetained: {
|
case ARCInstKind::LoadWeakRetained: {
|
||||||
// If this is loading from the same pointer, replace this load's value
|
// If this is loading from the same pointer, replace this load's value
|
||||||
// with that one.
|
// with that one.
|
||||||
CallInst *Call = cast<CallInst>(Inst);
|
CallInst *Call = cast<CallInst>(Inst);
|
||||||
|
@ -2674,7 +2677,7 @@ void ObjCARCOpt::OptimizeWeakCalls(Function &F) {
|
||||||
case AliasAnalysis::MustAlias:
|
case AliasAnalysis::MustAlias:
|
||||||
Changed = true;
|
Changed = true;
|
||||||
// If the load has a builtin retain, insert a plain retain for it.
|
// 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);
|
Constant *Decl = EP.get(ARCRuntimeEntryPoints::EPT_Retain);
|
||||||
CallInst *CI = CallInst::Create(Decl, EarlierCall, "", Call);
|
CallInst *CI = CallInst::Create(Decl, EarlierCall, "", Call);
|
||||||
CI->setTailCall();
|
CI->setTailCall();
|
||||||
|
@ -2691,8 +2694,8 @@ void ObjCARCOpt::OptimizeWeakCalls(Function &F) {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case IC_StoreWeak:
|
case ARCInstKind::StoreWeak:
|
||||||
case IC_InitWeak: {
|
case ARCInstKind::InitWeak: {
|
||||||
// If this is storing to the same pointer and has the same size etc.
|
// If this is storing to the same pointer and has the same size etc.
|
||||||
// replace this load's value with the stored value.
|
// replace this load's value with the stored value.
|
||||||
CallInst *Call = cast<CallInst>(Inst);
|
CallInst *Call = cast<CallInst>(Inst);
|
||||||
|
@ -2703,7 +2706,7 @@ void ObjCARCOpt::OptimizeWeakCalls(Function &F) {
|
||||||
case AliasAnalysis::MustAlias:
|
case AliasAnalysis::MustAlias:
|
||||||
Changed = true;
|
Changed = true;
|
||||||
// If the load has a builtin retain, insert a plain retain for it.
|
// 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);
|
Constant *Decl = EP.get(ARCRuntimeEntryPoints::EPT_Retain);
|
||||||
CallInst *CI = CallInst::Create(Decl, EarlierCall, "", Call);
|
CallInst *CI = CallInst::Create(Decl, EarlierCall, "", Call);
|
||||||
CI->setTailCall();
|
CI->setTailCall();
|
||||||
|
@ -2720,14 +2723,14 @@ void ObjCARCOpt::OptimizeWeakCalls(Function &F) {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case IC_MoveWeak:
|
case ARCInstKind::MoveWeak:
|
||||||
case IC_CopyWeak:
|
case ARCInstKind::CopyWeak:
|
||||||
// TOOD: Grab the copied value.
|
// TOOD: Grab the copied value.
|
||||||
goto clobbered;
|
goto clobbered;
|
||||||
case IC_AutoreleasepoolPush:
|
case ARCInstKind::AutoreleasepoolPush:
|
||||||
case IC_None:
|
case ARCInstKind::None:
|
||||||
case IC_IntrinsicUser:
|
case ARCInstKind::IntrinsicUser:
|
||||||
case IC_User:
|
case ARCInstKind::User:
|
||||||
// Weak pointers are only modified through the weak entry points
|
// Weak pointers are only modified through the weak entry points
|
||||||
// (and arbitrary calls, which could call the weak entry points).
|
// (and arbitrary calls, which could call the weak entry points).
|
||||||
break;
|
break;
|
||||||
|
@ -2743,8 +2746,8 @@ void ObjCARCOpt::OptimizeWeakCalls(Function &F) {
|
||||||
// the alloca and all its users can be zapped.
|
// the alloca and all its users can be zapped.
|
||||||
for (inst_iterator I = inst_begin(&F), E = inst_end(&F); I != E; ) {
|
for (inst_iterator I = inst_begin(&F), E = inst_end(&F); I != E; ) {
|
||||||
Instruction *Inst = &*I++;
|
Instruction *Inst = &*I++;
|
||||||
InstructionClass Class = GetBasicInstructionClass(Inst);
|
ARCInstKind Class = GetBasicARCInstKind(Inst);
|
||||||
if (Class != IC_DestroyWeak)
|
if (Class != ARCInstKind::DestroyWeak)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
CallInst *Call = cast<CallInst>(Inst);
|
CallInst *Call = cast<CallInst>(Inst);
|
||||||
|
@ -2752,10 +2755,10 @@ void ObjCARCOpt::OptimizeWeakCalls(Function &F) {
|
||||||
if (AllocaInst *Alloca = dyn_cast<AllocaInst>(Arg)) {
|
if (AllocaInst *Alloca = dyn_cast<AllocaInst>(Arg)) {
|
||||||
for (User *U : Alloca->users()) {
|
for (User *U : Alloca->users()) {
|
||||||
const Instruction *UserInst = cast<Instruction>(U);
|
const Instruction *UserInst = cast<Instruction>(U);
|
||||||
switch (GetBasicInstructionClass(UserInst)) {
|
switch (GetBasicARCInstKind(UserInst)) {
|
||||||
case IC_InitWeak:
|
case ARCInstKind::InitWeak:
|
||||||
case IC_StoreWeak:
|
case ARCInstKind::StoreWeak:
|
||||||
case IC_DestroyWeak:
|
case ARCInstKind::DestroyWeak:
|
||||||
continue;
|
continue;
|
||||||
default:
|
default:
|
||||||
goto done;
|
goto done;
|
||||||
|
@ -2764,13 +2767,13 @@ void ObjCARCOpt::OptimizeWeakCalls(Function &F) {
|
||||||
Changed = true;
|
Changed = true;
|
||||||
for (auto UI = Alloca->user_begin(), UE = Alloca->user_end(); UI != UE;) {
|
for (auto UI = Alloca->user_begin(), UE = Alloca->user_end(); UI != UE;) {
|
||||||
CallInst *UserInst = cast<CallInst>(*UI++);
|
CallInst *UserInst = cast<CallInst>(*UI++);
|
||||||
switch (GetBasicInstructionClass(UserInst)) {
|
switch (GetBasicARCInstKind(UserInst)) {
|
||||||
case IC_InitWeak:
|
case ARCInstKind::InitWeak:
|
||||||
case IC_StoreWeak:
|
case ARCInstKind::StoreWeak:
|
||||||
// These functions return their second argument.
|
// These functions return their second argument.
|
||||||
UserInst->replaceAllUsesWith(UserInst->getArgOperand(1));
|
UserInst->replaceAllUsesWith(UserInst->getArgOperand(1));
|
||||||
break;
|
break;
|
||||||
case IC_DestroyWeak:
|
case ARCInstKind::DestroyWeak:
|
||||||
// No return value.
|
// No return value.
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -2833,8 +2836,8 @@ HasSafePathToPredecessorCall(const Value *Arg, Instruction *Retain,
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Check that the call is a regular call.
|
// Check that the call is a regular call.
|
||||||
InstructionClass Class = GetBasicInstructionClass(Call);
|
ARCInstKind Class = GetBasicARCInstKind(Call);
|
||||||
if (Class != IC_CallOrUser && Class != IC_Call)
|
if (Class != ARCInstKind::CallOrUser && Class != ARCInstKind::Call)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -2858,8 +2861,7 @@ FindPredecessorRetainWithSafePath(const Value *Arg, BasicBlock *BB,
|
||||||
dyn_cast_or_null<CallInst>(*DepInsts.begin());
|
dyn_cast_or_null<CallInst>(*DepInsts.begin());
|
||||||
|
|
||||||
// Check that we found a retain with the same argument.
|
// Check that we found a retain with the same argument.
|
||||||
if (!Retain ||
|
if (!Retain || !IsRetain(GetBasicARCInstKind(Retain)) ||
|
||||||
!IsRetain(GetBasicInstructionClass(Retain)) ||
|
|
||||||
GetArgRCIdentityRoot(Retain) != Arg) {
|
GetArgRCIdentityRoot(Retain) != Arg) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
@ -2885,7 +2887,7 @@ FindPredecessorAutoreleaseWithSafePath(const Value *Arg, BasicBlock *BB,
|
||||||
dyn_cast_or_null<CallInst>(*DepInsts.begin());
|
dyn_cast_or_null<CallInst>(*DepInsts.begin());
|
||||||
if (!Autorelease)
|
if (!Autorelease)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
InstructionClass AutoreleaseClass = GetBasicInstructionClass(Autorelease);
|
ARCInstKind AutoreleaseClass = GetBasicARCInstKind(Autorelease);
|
||||||
if (!IsAutorelease(AutoreleaseClass))
|
if (!IsAutorelease(AutoreleaseClass))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
if (GetArgRCIdentityRoot(Autorelease) != Arg)
|
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; ) {
|
for (inst_iterator I = inst_begin(&F), E = inst_end(&F); I != E; ) {
|
||||||
Instruction *Inst = &*I++;
|
Instruction *Inst = &*I++;
|
||||||
switch (GetBasicInstructionClass(Inst)) {
|
switch (GetBasicARCInstKind(Inst)) {
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
case IC_Retain:
|
case ARCInstKind::Retain:
|
||||||
++NumRetains;
|
++NumRetains;
|
||||||
break;
|
break;
|
||||||
case IC_Release:
|
case ARCInstKind::Release:
|
||||||
++NumReleases;
|
++NumReleases;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -3052,27 +3054,27 @@ bool ObjCARCOpt::runOnFunction(Function &F) {
|
||||||
OptimizeIndividualCalls(F);
|
OptimizeIndividualCalls(F);
|
||||||
|
|
||||||
// Optimizations for weak pointers.
|
// Optimizations for weak pointers.
|
||||||
if (UsedInThisFunction & ((1 << IC_LoadWeak) |
|
if (UsedInThisFunction & ((1 << unsigned(ARCInstKind::LoadWeak)) |
|
||||||
(1 << IC_LoadWeakRetained) |
|
(1 << unsigned(ARCInstKind::LoadWeakRetained)) |
|
||||||
(1 << IC_StoreWeak) |
|
(1 << unsigned(ARCInstKind::StoreWeak)) |
|
||||||
(1 << IC_InitWeak) |
|
(1 << unsigned(ARCInstKind::InitWeak)) |
|
||||||
(1 << IC_CopyWeak) |
|
(1 << unsigned(ARCInstKind::CopyWeak)) |
|
||||||
(1 << IC_MoveWeak) |
|
(1 << unsigned(ARCInstKind::MoveWeak)) |
|
||||||
(1 << IC_DestroyWeak)))
|
(1 << unsigned(ARCInstKind::DestroyWeak))))
|
||||||
OptimizeWeakCalls(F);
|
OptimizeWeakCalls(F);
|
||||||
|
|
||||||
// Optimizations for retain+release pairs.
|
// Optimizations for retain+release pairs.
|
||||||
if (UsedInThisFunction & ((1 << IC_Retain) |
|
if (UsedInThisFunction & ((1 << unsigned(ARCInstKind::Retain)) |
|
||||||
(1 << IC_RetainRV) |
|
(1 << unsigned(ARCInstKind::RetainRV)) |
|
||||||
(1 << IC_RetainBlock)))
|
(1 << unsigned(ARCInstKind::RetainBlock))))
|
||||||
if (UsedInThisFunction & (1 << IC_Release))
|
if (UsedInThisFunction & (1 << unsigned(ARCInstKind::Release)))
|
||||||
// Run OptimizeSequences until it either stops making changes or
|
// Run OptimizeSequences until it either stops making changes or
|
||||||
// no retain+release pair nesting is detected.
|
// no retain+release pair nesting is detected.
|
||||||
while (OptimizeSequences(F)) {}
|
while (OptimizeSequences(F)) {}
|
||||||
|
|
||||||
// Optimizations if objc_autorelease is used.
|
// Optimizations if objc_autorelease is used.
|
||||||
if (UsedInThisFunction & ((1 << IC_Autorelease) |
|
if (UsedInThisFunction & ((1 << unsigned(ARCInstKind::Autorelease)) |
|
||||||
(1 << IC_AutoreleaseRV)))
|
(1 << unsigned(ARCInstKind::AutoreleaseRV))))
|
||||||
OptimizeReturns(F);
|
OptimizeReturns(F);
|
||||||
|
|
||||||
// Gather statistics after optimization.
|
// 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