| //===- ARCInstKind.cpp - ObjC ARC Optimization ----------------------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| /// \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/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::UnsafeClaimRV: | |
| return OS << "ARCInstKind::UnsafeClaimRV"; | |
| 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) { | |
| Intrinsic::ID ID = F->getIntrinsicID(); | |
| switch (ID) { | |
| default: | |
| return ARCInstKind::CallOrUser; | |
| case Intrinsic::objc_autorelease: | |
| return ARCInstKind::Autorelease; | |
| case Intrinsic::objc_autoreleasePoolPop: | |
| return ARCInstKind::AutoreleasepoolPop; | |
| case Intrinsic::objc_autoreleasePoolPush: | |
| return ARCInstKind::AutoreleasepoolPush; | |
| case Intrinsic::objc_autoreleaseReturnValue: | |
| return ARCInstKind::AutoreleaseRV; | |
| case Intrinsic::objc_copyWeak: | |
| return ARCInstKind::CopyWeak; | |
| case Intrinsic::objc_destroyWeak: | |
| return ARCInstKind::DestroyWeak; | |
| case Intrinsic::objc_initWeak: | |
| return ARCInstKind::InitWeak; | |
| case Intrinsic::objc_loadWeak: | |
| return ARCInstKind::LoadWeak; | |
| case Intrinsic::objc_loadWeakRetained: | |
| return ARCInstKind::LoadWeakRetained; | |
| case Intrinsic::objc_moveWeak: | |
| return ARCInstKind::MoveWeak; | |
| case Intrinsic::objc_release: | |
| return ARCInstKind::Release; | |
| case Intrinsic::objc_retain: | |
| return ARCInstKind::Retain; | |
| case Intrinsic::objc_retainAutorelease: | |
| return ARCInstKind::FusedRetainAutorelease; | |
| case Intrinsic::objc_retainAutoreleaseReturnValue: | |
| return ARCInstKind::FusedRetainAutoreleaseRV; | |
| case Intrinsic::objc_retainAutoreleasedReturnValue: | |
| return ARCInstKind::RetainRV; | |
| case Intrinsic::objc_retainBlock: | |
| return ARCInstKind::RetainBlock; | |
| case Intrinsic::objc_storeStrong: | |
| return ARCInstKind::StoreStrong; | |
| case Intrinsic::objc_storeWeak: | |
| return ARCInstKind::StoreWeak; | |
| case Intrinsic::objc_clang_arc_use: | |
| return ARCInstKind::IntrinsicUser; | |
| case Intrinsic::objc_unsafeClaimAutoreleasedReturnValue: | |
| return ARCInstKind::UnsafeClaimRV; | |
| case Intrinsic::objc_retainedObject: | |
| return ARCInstKind::NoopCast; | |
| case Intrinsic::objc_unretainedObject: | |
| return ARCInstKind::NoopCast; | |
| case Intrinsic::objc_unretainedPointer: | |
| return ARCInstKind::NoopCast; | |
| case Intrinsic::objc_retain_autorelease: | |
| return ARCInstKind::FusedRetainAutorelease; | |
| case Intrinsic::objc_sync_enter: | |
| return ARCInstKind::User; | |
| case Intrinsic::objc_sync_exit: | |
| return ARCInstKind::User; | |
| case Intrinsic::objc_clang_arc_noop_use: | |
| case Intrinsic::objc_arc_annotation_topdown_bbstart: | |
| case Intrinsic::objc_arc_annotation_topdown_bbend: | |
| case Intrinsic::objc_arc_annotation_bottomup_bbstart: | |
| case Intrinsic::objc_arc_annotation_bottomup_bbend: | |
| // 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. | |
| return ARCInstKind::None; | |
| } | |
| } | |
| // A list 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 list 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 (const Use &U : I->operands()) | |
| if (IsPotentialRetainableObjPtr(U)) | |
| 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::UnsafeClaimRV: | |
| 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::UnsafeClaimRV: | |
| 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::UnsafeClaimRV: | |
| 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::UnsafeClaimRV: | |
| 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::UnsafeClaimRV: | |
| 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 do nothing if | |
| /// passed a global variable. | |
| bool llvm::objcarc::IsNoopOnGlobal(ARCInstKind Class) { | |
| switch (Class) { | |
| case ARCInstKind::Retain: | |
| case ARCInstKind::RetainRV: | |
| case ARCInstKind::UnsafeClaimRV: | |
| case ARCInstKind::Release: | |
| case ARCInstKind::Autorelease: | |
| case ARCInstKind::AutoreleaseRV: | |
| case ARCInstKind::RetainBlock: | |
| case ARCInstKind::FusedRetainAutorelease: | |
| case ARCInstKind::FusedRetainAutoreleaseRV: | |
| return true; | |
| 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::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::UnsafeClaimRV: | |
| 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::UnsafeClaimRV: | |
| 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::UnsafeClaimRV: | |
| 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::UnsafeClaimRV: | |
| 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::UnsafeClaimRV: | |
| return true; | |
| } | |
| llvm_unreachable("covered switch isn't covered?"); | |
| } |