| //===- 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 "llvm/Analysis/ObjCARCInstKind.h" |
| #include "llvm/ADT/StringSwitch.h" |
| #include "llvm/Analysis/ObjCARCAnalysisUtils.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::ClaimRV: |
| return OS << "ARCInstKind::ClaimRV"; |
| 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. |
| PointerType *PTy = dyn_cast<PointerType>(A0->getType()); |
| if (!PTy) |
| return ARCInstKind::CallOrUser; |
| |
| 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_unsafeClaimAutoreleasedReturnValue", ARCInstKind::ClaimRV) |
| .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); |
| |
| // Anything else with one argument. |
| return 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; |
| } |
| |
| // A whitelist of intrinsics that we know do not use objc pointers or decrement |
| // ref counts. |
| static bool isInertIntrinsic(unsigned ID) { |
| // TODO: Make this into a covered switch. |
| switch (ID) { |
| case Intrinsic::returnaddress: |
| case Intrinsic::addressofreturnaddress: |
| 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: |
| case Intrinsic::dbg_label: |
| // Short cut: Some intrinsics obviously don't use ObjC pointers. |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| // A whitelist of intrinsics that we know do not use objc pointers or decrement |
| // ref counts. |
| static bool isUseOnlyIntrinsic(unsigned ID) { |
| // We are conservative and even though intrinsics are unlikely to touch |
| // reference counts, we white list them for safety. |
| // |
| // TODO: Expand this into a covered switch. There is a lot more here. |
| switch (ID) { |
| case Intrinsic::memcpy: |
| case Intrinsic::memmove: |
| case Intrinsic::memset: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| /// 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); |
| // See if we have a function that we know something about. |
| if (const Function *F = CI->getCalledFunction()) { |
| ARCInstKind Class = GetFunctionClass(F); |
| if (Class != ARCInstKind::CallOrUser) |
| return Class; |
| Intrinsic::ID ID = F->getIntrinsicID(); |
| if (isInertIntrinsic(ID)) |
| return ARCInstKind::None; |
| if (isUseOnlyIntrinsic(ID)) |
| return ARCInstKind::User; |
| } |
| |
| // Otherwise, be conservative. |
| return GetCallSiteClass(CI); |
| } |
| case Instruction::Invoke: |
| // Otherwise, be conservative. |
| 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; |
| } |
| |
| /// Test if the given class is a kind of user. |
| bool llvm::objcarc::IsUser(ARCInstKind Class) { |
| switch (Class) { |
| case ARCInstKind::User: |
| case ARCInstKind::CallOrUser: |
| case ARCInstKind::IntrinsicUser: |
| return true; |
| case ARCInstKind::Retain: |
| case ARCInstKind::RetainRV: |
| case ARCInstKind::RetainBlock: |
| case ARCInstKind::Release: |
| case ARCInstKind::Autorelease: |
| case ARCInstKind::AutoreleaseRV: |
| case ARCInstKind::AutoreleasepoolPush: |
| case ARCInstKind::AutoreleasepoolPop: |
| case ARCInstKind::NoopCast: |
| case ARCInstKind::FusedRetainAutorelease: |
| case ARCInstKind::FusedRetainAutoreleaseRV: |
| case ARCInstKind::LoadWeakRetained: |
| case ARCInstKind::StoreWeak: |
| case ARCInstKind::InitWeak: |
| case ARCInstKind::LoadWeak: |
| case ARCInstKind::MoveWeak: |
| case ARCInstKind::CopyWeak: |
| case ARCInstKind::DestroyWeak: |
| case ARCInstKind::StoreStrong: |
| case ARCInstKind::Call: |
| case ARCInstKind::None: |
| case ARCInstKind::ClaimRV: |
| return false; |
| } |
| llvm_unreachable("covered switch isn't covered?"); |
| } |
| |
| /// Test if the given class is objc_retain or equivalent. |
| bool llvm::objcarc::IsRetain(ARCInstKind Class) { |
| switch (Class) { |
| case ARCInstKind::Retain: |
| case ARCInstKind::RetainRV: |
| return true; |
| // I believe we treat retain block as not a retain since it can copy its |
| // block. |
| case ARCInstKind::RetainBlock: |
| case ARCInstKind::Release: |
| case ARCInstKind::Autorelease: |
| case ARCInstKind::AutoreleaseRV: |
| case ARCInstKind::AutoreleasepoolPush: |
| case ARCInstKind::AutoreleasepoolPop: |
| case ARCInstKind::NoopCast: |
| case ARCInstKind::FusedRetainAutorelease: |
| case ARCInstKind::FusedRetainAutoreleaseRV: |
| case ARCInstKind::LoadWeakRetained: |
| case ARCInstKind::StoreWeak: |
| case ARCInstKind::InitWeak: |
| case ARCInstKind::LoadWeak: |
| case ARCInstKind::MoveWeak: |
| case ARCInstKind::CopyWeak: |
| case ARCInstKind::DestroyWeak: |
| case ARCInstKind::StoreStrong: |
| case ARCInstKind::IntrinsicUser: |
| case ARCInstKind::CallOrUser: |
| case ARCInstKind::Call: |
| case ARCInstKind::User: |
| case ARCInstKind::None: |
| case ARCInstKind::ClaimRV: |
| return false; |
| } |
| llvm_unreachable("covered switch isn't covered?"); |
| } |
| |
| /// Test if the given class is objc_autorelease or equivalent. |
| bool llvm::objcarc::IsAutorelease(ARCInstKind Class) { |
| switch (Class) { |
| case ARCInstKind::Autorelease: |
| case ARCInstKind::AutoreleaseRV: |
| return true; |
| case ARCInstKind::Retain: |
| case ARCInstKind::RetainRV: |
| case ARCInstKind::ClaimRV: |
| case ARCInstKind::RetainBlock: |
| case ARCInstKind::Release: |
| case ARCInstKind::AutoreleasepoolPush: |
| case ARCInstKind::AutoreleasepoolPop: |
| case ARCInstKind::NoopCast: |
| case ARCInstKind::FusedRetainAutorelease: |
| case ARCInstKind::FusedRetainAutoreleaseRV: |
| case ARCInstKind::LoadWeakRetained: |
| case ARCInstKind::StoreWeak: |
| case ARCInstKind::InitWeak: |
| case ARCInstKind::LoadWeak: |
| case ARCInstKind::MoveWeak: |
| case ARCInstKind::CopyWeak: |
| case ARCInstKind::DestroyWeak: |
| case ARCInstKind::StoreStrong: |
| case ARCInstKind::IntrinsicUser: |
| case ARCInstKind::CallOrUser: |
| case ARCInstKind::Call: |
| case ARCInstKind::User: |
| case ARCInstKind::None: |
| return false; |
| } |
| llvm_unreachable("covered switch isn't covered?"); |
| } |
| |
| /// Test if the given class represents instructions which return their |
| /// argument verbatim. |
| bool llvm::objcarc::IsForwarding(ARCInstKind Class) { |
| switch (Class) { |
| case ARCInstKind::Retain: |
| case ARCInstKind::RetainRV: |
| case ARCInstKind::ClaimRV: |
| case ARCInstKind::Autorelease: |
| case ARCInstKind::AutoreleaseRV: |
| case ARCInstKind::NoopCast: |
| return true; |
| case ARCInstKind::RetainBlock: |
| case ARCInstKind::Release: |
| case ARCInstKind::AutoreleasepoolPush: |
| case ARCInstKind::AutoreleasepoolPop: |
| case ARCInstKind::FusedRetainAutorelease: |
| case ARCInstKind::FusedRetainAutoreleaseRV: |
| case ARCInstKind::LoadWeakRetained: |
| case ARCInstKind::StoreWeak: |
| case ARCInstKind::InitWeak: |
| case ARCInstKind::LoadWeak: |
| case ARCInstKind::MoveWeak: |
| case ARCInstKind::CopyWeak: |
| case ARCInstKind::DestroyWeak: |
| case ARCInstKind::StoreStrong: |
| case ARCInstKind::IntrinsicUser: |
| case ARCInstKind::CallOrUser: |
| case ARCInstKind::Call: |
| case ARCInstKind::User: |
| case ARCInstKind::None: |
| return false; |
| } |
| llvm_unreachable("covered switch isn't covered?"); |
| } |
| |
| /// Test if the given class represents instructions which do nothing if |
| /// passed a null pointer. |
| bool llvm::objcarc::IsNoopOnNull(ARCInstKind Class) { |
| switch (Class) { |
| case ARCInstKind::Retain: |
| case ARCInstKind::RetainRV: |
| case ARCInstKind::ClaimRV: |
| case ARCInstKind::Release: |
| case ARCInstKind::Autorelease: |
| case ARCInstKind::AutoreleaseRV: |
| case ARCInstKind::RetainBlock: |
| return true; |
| case ARCInstKind::AutoreleasepoolPush: |
| case ARCInstKind::AutoreleasepoolPop: |
| case ARCInstKind::FusedRetainAutorelease: |
| case ARCInstKind::FusedRetainAutoreleaseRV: |
| case ARCInstKind::LoadWeakRetained: |
| case ARCInstKind::StoreWeak: |
| case ARCInstKind::InitWeak: |
| case ARCInstKind::LoadWeak: |
| case ARCInstKind::MoveWeak: |
| case ARCInstKind::CopyWeak: |
| case ARCInstKind::DestroyWeak: |
| case ARCInstKind::StoreStrong: |
| case ARCInstKind::IntrinsicUser: |
| case ARCInstKind::CallOrUser: |
| case ARCInstKind::Call: |
| case ARCInstKind::User: |
| case ARCInstKind::None: |
| case ARCInstKind::NoopCast: |
| return false; |
| } |
| llvm_unreachable("covered switch isn't covered?"); |
| } |
| |
| /// Test if the given class represents instructions which are always safe |
| /// to mark with the "tail" keyword. |
| bool llvm::objcarc::IsAlwaysTail(ARCInstKind Class) { |
| // ARCInstKind::RetainBlock may be given a stack argument. |
| switch (Class) { |
| case ARCInstKind::Retain: |
| case ARCInstKind::RetainRV: |
| case ARCInstKind::ClaimRV: |
| case ARCInstKind::AutoreleaseRV: |
| return true; |
| case ARCInstKind::Release: |
| case ARCInstKind::Autorelease: |
| case ARCInstKind::RetainBlock: |
| case ARCInstKind::AutoreleasepoolPush: |
| case ARCInstKind::AutoreleasepoolPop: |
| case ARCInstKind::FusedRetainAutorelease: |
| case ARCInstKind::FusedRetainAutoreleaseRV: |
| case ARCInstKind::LoadWeakRetained: |
| case ARCInstKind::StoreWeak: |
| case ARCInstKind::InitWeak: |
| case ARCInstKind::LoadWeak: |
| case ARCInstKind::MoveWeak: |
| case ARCInstKind::CopyWeak: |
| case ARCInstKind::DestroyWeak: |
| case ARCInstKind::StoreStrong: |
| case ARCInstKind::IntrinsicUser: |
| case ARCInstKind::CallOrUser: |
| case ARCInstKind::Call: |
| case ARCInstKind::User: |
| case ARCInstKind::None: |
| case ARCInstKind::NoopCast: |
| return false; |
| } |
| llvm_unreachable("covered switch isn't covered?"); |
| } |
| |
| /// Test if the given class represents instructions which are never safe |
| /// to mark with the "tail" keyword. |
| bool llvm::objcarc::IsNeverTail(ARCInstKind Class) { |
| /// It is never safe to tail call objc_autorelease since by tail calling |
| /// objc_autorelease: fast autoreleasing causing our object to be potentially |
| /// reclaimed from the autorelease pool which violates the semantics of |
| /// __autoreleasing types in ARC. |
| switch (Class) { |
| case ARCInstKind::Autorelease: |
| return true; |
| case ARCInstKind::Retain: |
| case ARCInstKind::RetainRV: |
| case ARCInstKind::ClaimRV: |
| case ARCInstKind::AutoreleaseRV: |
| case ARCInstKind::Release: |
| case ARCInstKind::RetainBlock: |
| case ARCInstKind::AutoreleasepoolPush: |
| case ARCInstKind::AutoreleasepoolPop: |
| case ARCInstKind::FusedRetainAutorelease: |
| case ARCInstKind::FusedRetainAutoreleaseRV: |
| case ARCInstKind::LoadWeakRetained: |
| case ARCInstKind::StoreWeak: |
| case ARCInstKind::InitWeak: |
| case ARCInstKind::LoadWeak: |
| case ARCInstKind::MoveWeak: |
| case ARCInstKind::CopyWeak: |
| case ARCInstKind::DestroyWeak: |
| case ARCInstKind::StoreStrong: |
| case ARCInstKind::IntrinsicUser: |
| case ARCInstKind::CallOrUser: |
| case ARCInstKind::Call: |
| case ARCInstKind::User: |
| case ARCInstKind::None: |
| case ARCInstKind::NoopCast: |
| return false; |
| } |
| llvm_unreachable("covered switch isn't covered?"); |
| } |
| |
| /// Test if the given class represents instructions which are always safe |
| /// to mark with the nounwind attribute. |
| bool llvm::objcarc::IsNoThrow(ARCInstKind Class) { |
| // objc_retainBlock is not nounwind because it calls user copy constructors |
| // which could theoretically throw. |
| switch (Class) { |
| case ARCInstKind::Retain: |
| case ARCInstKind::RetainRV: |
| case ARCInstKind::ClaimRV: |
| case ARCInstKind::Release: |
| case ARCInstKind::Autorelease: |
| case ARCInstKind::AutoreleaseRV: |
| case ARCInstKind::AutoreleasepoolPush: |
| case ARCInstKind::AutoreleasepoolPop: |
| return true; |
| case ARCInstKind::RetainBlock: |
| case ARCInstKind::FusedRetainAutorelease: |
| case ARCInstKind::FusedRetainAutoreleaseRV: |
| case ARCInstKind::LoadWeakRetained: |
| case ARCInstKind::StoreWeak: |
| case ARCInstKind::InitWeak: |
| case ARCInstKind::LoadWeak: |
| case ARCInstKind::MoveWeak: |
| case ARCInstKind::CopyWeak: |
| case ARCInstKind::DestroyWeak: |
| case ARCInstKind::StoreStrong: |
| case ARCInstKind::IntrinsicUser: |
| case ARCInstKind::CallOrUser: |
| case ARCInstKind::Call: |
| case ARCInstKind::User: |
| case ARCInstKind::None: |
| case ARCInstKind::NoopCast: |
| return false; |
| } |
| llvm_unreachable("covered switch isn't covered?"); |
| } |
| |
| /// Test whether the given instruction can autorelease any pointer or cause an |
| /// autoreleasepool pop. |
| /// |
| /// This means that it *could* interrupt the RV optimization. |
| bool llvm::objcarc::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; |
| case ARCInstKind::Retain: |
| case ARCInstKind::RetainRV: |
| case ARCInstKind::ClaimRV: |
| case ARCInstKind::Release: |
| case ARCInstKind::AutoreleasepoolPush: |
| case ARCInstKind::RetainBlock: |
| case ARCInstKind::LoadWeakRetained: |
| case ARCInstKind::StoreWeak: |
| case ARCInstKind::InitWeak: |
| case ARCInstKind::LoadWeak: |
| case ARCInstKind::MoveWeak: |
| case ARCInstKind::CopyWeak: |
| case ARCInstKind::DestroyWeak: |
| case ARCInstKind::StoreStrong: |
| case ARCInstKind::IntrinsicUser: |
| case ARCInstKind::User: |
| case ARCInstKind::None: |
| case ARCInstKind::NoopCast: |
| return false; |
| } |
| llvm_unreachable("covered switch isn't covered?"); |
| } |
| |
| bool llvm::objcarc::CanDecrementRefCount(ARCInstKind Kind) { |
| switch (Kind) { |
| case ARCInstKind::Retain: |
| case ARCInstKind::RetainRV: |
| case ARCInstKind::Autorelease: |
| case ARCInstKind::AutoreleaseRV: |
| case ARCInstKind::NoopCast: |
| case ARCInstKind::FusedRetainAutorelease: |
| case ARCInstKind::FusedRetainAutoreleaseRV: |
| case ARCInstKind::IntrinsicUser: |
| case ARCInstKind::User: |
| case ARCInstKind::None: |
| return false; |
| |
| // The cases below are conservative. |
| |
| // RetainBlock can result in user defined copy constructors being called |
| // implying releases may occur. |
| case ARCInstKind::RetainBlock: |
| case ARCInstKind::Release: |
| case ARCInstKind::AutoreleasepoolPush: |
| case ARCInstKind::AutoreleasepoolPop: |
| case ARCInstKind::LoadWeakRetained: |
| case ARCInstKind::StoreWeak: |
| case ARCInstKind::InitWeak: |
| case ARCInstKind::LoadWeak: |
| case ARCInstKind::MoveWeak: |
| case ARCInstKind::CopyWeak: |
| case ARCInstKind::DestroyWeak: |
| case ARCInstKind::StoreStrong: |
| case ARCInstKind::CallOrUser: |
| case ARCInstKind::Call: |
| case ARCInstKind::ClaimRV: |
| return true; |
| } |
| |
| llvm_unreachable("covered switch isn't covered?"); |
| } |