Path: blob/main/contrib/llvm-project/llvm/lib/Transforms/ObjCARC/PtrState.cpp
35266 views
//===- PtrState.cpp -------------------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "PtrState.h"9#include "DependencyAnalysis.h"10#include "ObjCARC.h"11#include "llvm/Analysis/ObjCARCAnalysisUtils.h"12#include "llvm/Analysis/ObjCARCInstKind.h"13#include "llvm/Analysis/ObjCARCUtil.h"14#include "llvm/IR/BasicBlock.h"15#include "llvm/IR/Instruction.h"16#include "llvm/IR/Instructions.h"17#include "llvm/IR/Value.h"18#include "llvm/Support/Casting.h"19#include "llvm/Support/Compiler.h"20#include "llvm/Support/Debug.h"21#include "llvm/Support/ErrorHandling.h"22#include "llvm/Support/raw_ostream.h"23#include <cassert>24#include <iterator>25#include <utility>2627using namespace llvm;28using namespace llvm::objcarc;2930#define DEBUG_TYPE "objc-arc-ptr-state"3132//===----------------------------------------------------------------------===//33// Utility34//===----------------------------------------------------------------------===//3536raw_ostream &llvm::objcarc::operator<<(raw_ostream &OS, const Sequence S) {37switch (S) {38case S_None:39return OS << "S_None";40case S_Retain:41return OS << "S_Retain";42case S_CanRelease:43return OS << "S_CanRelease";44case S_Use:45return OS << "S_Use";46case S_MovableRelease:47return OS << "S_MovableRelease";48case S_Stop:49return OS << "S_Stop";50}51llvm_unreachable("Unknown sequence type.");52}5354//===----------------------------------------------------------------------===//55// Sequence56//===----------------------------------------------------------------------===//5758static Sequence MergeSeqs(Sequence A, Sequence B, bool TopDown) {59// The easy cases.60if (A == B)61return A;62if (A == S_None || B == S_None)63return S_None;6465if (A > B)66std::swap(A, B);67if (TopDown) {68// Choose the side which is further along in the sequence.69if ((A == S_Retain || A == S_CanRelease) &&70(B == S_CanRelease || B == S_Use))71return B;72} else {73// Choose the side which is further along in the sequence.74if ((A == S_Use || A == S_CanRelease) &&75(B == S_Use || B == S_Stop || B == S_MovableRelease))76return A;77// If both sides are releases, choose the more conservative one.78if (A == S_Stop && B == S_MovableRelease)79return A;80}8182return S_None;83}8485//===----------------------------------------------------------------------===//86// RRInfo87//===----------------------------------------------------------------------===//8889void RRInfo::clear() {90KnownSafe = false;91IsTailCallRelease = false;92ReleaseMetadata = nullptr;93Calls.clear();94ReverseInsertPts.clear();95CFGHazardAfflicted = false;96}9798bool RRInfo::Merge(const RRInfo &Other) {99// Conservatively merge the ReleaseMetadata information.100if (ReleaseMetadata != Other.ReleaseMetadata)101ReleaseMetadata = nullptr;102103// Conservatively merge the boolean state.104KnownSafe &= Other.KnownSafe;105IsTailCallRelease &= Other.IsTailCallRelease;106CFGHazardAfflicted |= Other.CFGHazardAfflicted;107108// Merge the call sets.109Calls.insert(Other.Calls.begin(), Other.Calls.end());110111// Merge the insert point sets. If there are any differences,112// that makes this a partial merge.113bool Partial = ReverseInsertPts.size() != Other.ReverseInsertPts.size();114for (Instruction *Inst : Other.ReverseInsertPts)115Partial |= ReverseInsertPts.insert(Inst).second;116return Partial;117}118119//===----------------------------------------------------------------------===//120// PtrState121//===----------------------------------------------------------------------===//122123void PtrState::SetKnownPositiveRefCount() {124LLVM_DEBUG(dbgs() << " Setting Known Positive.\n");125KnownPositiveRefCount = true;126}127128void PtrState::ClearKnownPositiveRefCount() {129LLVM_DEBUG(dbgs() << " Clearing Known Positive.\n");130KnownPositiveRefCount = false;131}132133void PtrState::SetSeq(Sequence NewSeq) {134LLVM_DEBUG(dbgs() << " Old: " << GetSeq() << "; New: " << NewSeq135<< "\n");136Seq = NewSeq;137}138139void PtrState::ResetSequenceProgress(Sequence NewSeq) {140LLVM_DEBUG(dbgs() << " Resetting sequence progress.\n");141SetSeq(NewSeq);142Partial = false;143RRI.clear();144}145146void PtrState::Merge(const PtrState &Other, bool TopDown) {147Seq = MergeSeqs(GetSeq(), Other.GetSeq(), TopDown);148KnownPositiveRefCount &= Other.KnownPositiveRefCount;149150// If we're not in a sequence (anymore), drop all associated state.151if (Seq == S_None) {152Partial = false;153RRI.clear();154} else if (Partial || Other.Partial) {155// If we're doing a merge on a path that's previously seen a partial156// merge, conservatively drop the sequence, to avoid doing partial157// RR elimination. If the branch predicates for the two merge differ,158// mixing them is unsafe.159ClearSequenceProgress();160} else {161// Otherwise merge the other PtrState's RRInfo into our RRInfo. At this162// point, we know that currently we are not partial. Stash whether or not163// the merge operation caused us to undergo a partial merging of reverse164// insertion points.165Partial = RRI.Merge(Other.RRI);166}167}168169//===----------------------------------------------------------------------===//170// BottomUpPtrState171//===----------------------------------------------------------------------===//172173bool BottomUpPtrState::InitBottomUp(ARCMDKindCache &Cache, Instruction *I) {174// If we see two releases in a row on the same pointer. If so, make175// a note, and we'll cicle back to revisit it after we've176// hopefully eliminated the second release, which may allow us to177// eliminate the first release too.178// Theoretically we could implement removal of nested retain+release179// pairs by making PtrState hold a stack of states, but this is180// simple and avoids adding overhead for the non-nested case.181bool NestingDetected = false;182if (GetSeq() == S_MovableRelease) {183LLVM_DEBUG(184dbgs() << " Found nested releases (i.e. a release pair)\n");185NestingDetected = true;186}187188MDNode *ReleaseMetadata =189I->getMetadata(Cache.get(ARCMDKindID::ImpreciseRelease));190Sequence NewSeq = ReleaseMetadata ? S_MovableRelease : S_Stop;191ResetSequenceProgress(NewSeq);192if (NewSeq == S_Stop)193InsertReverseInsertPt(I);194SetReleaseMetadata(ReleaseMetadata);195SetKnownSafe(HasKnownPositiveRefCount());196SetTailCallRelease(cast<CallInst>(I)->isTailCall());197InsertCall(I);198SetKnownPositiveRefCount();199return NestingDetected;200}201202bool BottomUpPtrState::MatchWithRetain() {203SetKnownPositiveRefCount();204205Sequence OldSeq = GetSeq();206switch (OldSeq) {207case S_Stop:208case S_MovableRelease:209case S_Use:210// If OldSeq is not S_Use or OldSeq is S_Use and we are tracking an211// imprecise release, clear our reverse insertion points.212if (OldSeq != S_Use || IsTrackingImpreciseReleases())213ClearReverseInsertPts();214[[fallthrough]];215case S_CanRelease:216return true;217case S_None:218return false;219case S_Retain:220llvm_unreachable("bottom-up pointer in retain state!");221}222llvm_unreachable("Sequence unknown enum value");223}224225bool BottomUpPtrState::HandlePotentialAlterRefCount(Instruction *Inst,226const Value *Ptr,227ProvenanceAnalysis &PA,228ARCInstKind Class) {229Sequence S = GetSeq();230231// Check for possible releases.232if (!CanDecrementRefCount(Inst, Ptr, PA, Class))233return false;234235LLVM_DEBUG(dbgs() << " CanAlterRefCount: Seq: " << S << "; "236<< *Ptr << "\n");237switch (S) {238case S_Use:239SetSeq(S_CanRelease);240return true;241case S_CanRelease:242case S_MovableRelease:243case S_Stop:244case S_None:245return false;246case S_Retain:247llvm_unreachable("bottom-up pointer in retain state!");248}249llvm_unreachable("Sequence unknown enum value");250}251252void BottomUpPtrState::HandlePotentialUse(BasicBlock *BB, Instruction *Inst,253const Value *Ptr,254ProvenanceAnalysis &PA,255ARCInstKind Class) {256auto SetSeqAndInsertReverseInsertPt = [&](Sequence NewSeq){257assert(!HasReverseInsertPts());258SetSeq(NewSeq);259// If this is an invoke instruction, we're scanning it as part of260// one of its successor blocks, since we can't insert code after it261// in its own block, and we don't want to split critical edges.262BasicBlock::iterator InsertAfter;263if (isa<InvokeInst>(Inst)) {264const auto IP = BB->getFirstInsertionPt();265InsertAfter = IP == BB->end() ? std::prev(BB->end()) : IP;266if (isa<CatchSwitchInst>(InsertAfter))267// A catchswitch must be the only non-phi instruction in its basic268// block, so attempting to insert an instruction into such a block would269// produce invalid IR.270SetCFGHazardAfflicted(true);271} else {272InsertAfter = std::next(Inst->getIterator());273}274275if (InsertAfter != BB->end())276InsertAfter = skipDebugIntrinsics(InsertAfter);277278InsertReverseInsertPt(&*InsertAfter);279280// Don't insert anything between a call/invoke with operand bundle281// "clang.arc.attachedcall" and the retainRV/claimRV call that uses the call282// result.283if (auto *CB = dyn_cast<CallBase>(Inst))284if (objcarc::hasAttachedCallOpBundle(CB))285SetCFGHazardAfflicted(true);286};287288// Check for possible direct uses.289switch (GetSeq()) {290case S_MovableRelease:291if (CanUse(Inst, Ptr, PA, Class)) {292LLVM_DEBUG(dbgs() << " CanUse: Seq: " << GetSeq() << "; "293<< *Ptr << "\n");294SetSeqAndInsertReverseInsertPt(S_Use);295} else if (const auto *Call = getreturnRVOperand(*Inst, Class)) {296if (CanUse(Call, Ptr, PA, GetBasicARCInstKind(Call))) {297LLVM_DEBUG(dbgs() << " ReleaseUse: Seq: " << GetSeq() << "; "298<< *Ptr << "\n");299SetSeqAndInsertReverseInsertPt(S_Stop);300}301}302break;303case S_Stop:304if (CanUse(Inst, Ptr, PA, Class)) {305LLVM_DEBUG(dbgs() << " PreciseStopUse: Seq: " << GetSeq()306<< "; " << *Ptr << "\n");307SetSeq(S_Use);308}309break;310case S_CanRelease:311case S_Use:312case S_None:313break;314case S_Retain:315llvm_unreachable("bottom-up pointer in retain state!");316}317}318319//===----------------------------------------------------------------------===//320// TopDownPtrState321//===----------------------------------------------------------------------===//322323bool TopDownPtrState::InitTopDown(ARCInstKind Kind, Instruction *I) {324bool NestingDetected = false;325// Don't do retain+release tracking for ARCInstKind::RetainRV, because326// it's327// better to let it remain as the first instruction after a call.328if (Kind != ARCInstKind::RetainRV) {329// If we see two retains in a row on the same pointer. If so, make330// a note, and we'll cicle back to revisit it after we've331// hopefully eliminated the second retain, which may allow us to332// eliminate the first retain too.333// Theoretically we could implement removal of nested retain+release334// pairs by making PtrState hold a stack of states, but this is335// simple and avoids adding overhead for the non-nested case.336if (GetSeq() == S_Retain)337NestingDetected = true;338339ResetSequenceProgress(S_Retain);340SetKnownSafe(HasKnownPositiveRefCount());341InsertCall(I);342}343344SetKnownPositiveRefCount();345return NestingDetected;346}347348bool TopDownPtrState::MatchWithRelease(ARCMDKindCache &Cache,349Instruction *Release) {350ClearKnownPositiveRefCount();351352Sequence OldSeq = GetSeq();353354MDNode *ReleaseMetadata =355Release->getMetadata(Cache.get(ARCMDKindID::ImpreciseRelease));356357switch (OldSeq) {358case S_Retain:359case S_CanRelease:360if (OldSeq == S_Retain || ReleaseMetadata != nullptr)361ClearReverseInsertPts();362[[fallthrough]];363case S_Use:364SetReleaseMetadata(ReleaseMetadata);365SetTailCallRelease(cast<CallInst>(Release)->isTailCall());366return true;367case S_None:368return false;369case S_Stop:370case S_MovableRelease:371llvm_unreachable("top-down pointer in bottom up state!");372}373llvm_unreachable("Sequence unknown enum value");374}375376bool TopDownPtrState::HandlePotentialAlterRefCount(377Instruction *Inst, const Value *Ptr, ProvenanceAnalysis &PA,378ARCInstKind Class, const BundledRetainClaimRVs &BundledRVs) {379// Check for possible releases. Treat clang.arc.use as a releasing instruction380// to prevent sinking a retain past it.381if (!CanDecrementRefCount(Inst, Ptr, PA, Class) &&382Class != ARCInstKind::IntrinsicUser)383return false;384385LLVM_DEBUG(dbgs() << " CanAlterRefCount: Seq: " << GetSeq() << "; "386<< *Ptr << "\n");387ClearKnownPositiveRefCount();388switch (GetSeq()) {389case S_Retain:390SetSeq(S_CanRelease);391assert(!HasReverseInsertPts());392InsertReverseInsertPt(Inst);393394// Don't insert anything between a call/invoke with operand bundle395// "clang.arc.attachedcall" and the retainRV/claimRV call that uses the call396// result.397if (BundledRVs.contains(Inst))398SetCFGHazardAfflicted(true);399400// One call can't cause a transition from S_Retain to S_CanRelease401// and S_CanRelease to S_Use. If we've made the first transition,402// we're done.403return true;404case S_Use:405case S_CanRelease:406case S_None:407return false;408case S_Stop:409case S_MovableRelease:410llvm_unreachable("top-down pointer in release state!");411}412llvm_unreachable("covered switch is not covered!?");413}414415void TopDownPtrState::HandlePotentialUse(Instruction *Inst, const Value *Ptr,416ProvenanceAnalysis &PA,417ARCInstKind Class) {418// Check for possible direct uses.419switch (GetSeq()) {420case S_CanRelease:421if (!CanUse(Inst, Ptr, PA, Class))422return;423LLVM_DEBUG(dbgs() << " CanUse: Seq: " << GetSeq() << "; "424<< *Ptr << "\n");425SetSeq(S_Use);426return;427case S_Retain:428case S_Use:429case S_None:430return;431case S_Stop:432case S_MovableRelease:433llvm_unreachable("top-down pointer in release state!");434}435}436437438