mirror of
https://github.com/RPCS3/llvm.git
synced 2025-05-22 13:26:03 +00:00

to reflect the new license. We understand that people may be surprised that we're moving the header entirely to discuss the new license. We checked this carefully with the Foundation's lawyer and we believe this is the correct approach. Essentially, all code in the project is now made available by the LLVM project under our new license, so you will see that the license headers include that license only. Some of our contributors have contributed code under our old license, and accordingly, we have retained a copy of our old license notice in the top-level files in each project and repository. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@351636 91177308-0d34-0410-b5e6-96231b3b80d8
433 lines
14 KiB
C++
433 lines
14 KiB
C++
//===- PtrState.cpp -------------------------------------------------------===//
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "PtrState.h"
|
|
#include "DependencyAnalysis.h"
|
|
#include "ObjCARC.h"
|
|
#include "llvm/Analysis/ObjCARCAnalysisUtils.h"
|
|
#include "llvm/Analysis/ObjCARCInstKind.h"
|
|
#include "llvm/IR/BasicBlock.h"
|
|
#include "llvm/IR/Instruction.h"
|
|
#include "llvm/IR/Instructions.h"
|
|
#include "llvm/IR/Value.h"
|
|
#include "llvm/Support/Casting.h"
|
|
#include "llvm/Support/Compiler.h"
|
|
#include "llvm/Support/Debug.h"
|
|
#include "llvm/Support/ErrorHandling.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
#include <cassert>
|
|
#include <iterator>
|
|
#include <utility>
|
|
|
|
using namespace llvm;
|
|
using namespace llvm::objcarc;
|
|
|
|
#define DEBUG_TYPE "objc-arc-ptr-state"
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Utility
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
raw_ostream &llvm::objcarc::operator<<(raw_ostream &OS, const Sequence S) {
|
|
switch (S) {
|
|
case S_None:
|
|
return OS << "S_None";
|
|
case S_Retain:
|
|
return OS << "S_Retain";
|
|
case S_CanRelease:
|
|
return OS << "S_CanRelease";
|
|
case S_Use:
|
|
return OS << "S_Use";
|
|
case S_Release:
|
|
return OS << "S_Release";
|
|
case S_MovableRelease:
|
|
return OS << "S_MovableRelease";
|
|
case S_Stop:
|
|
return OS << "S_Stop";
|
|
}
|
|
llvm_unreachable("Unknown sequence type.");
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Sequence
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
static Sequence MergeSeqs(Sequence A, Sequence B, bool TopDown) {
|
|
// The easy cases.
|
|
if (A == B)
|
|
return A;
|
|
if (A == S_None || B == S_None)
|
|
return S_None;
|
|
|
|
if (A > B)
|
|
std::swap(A, B);
|
|
if (TopDown) {
|
|
// Choose the side which is further along in the sequence.
|
|
if ((A == S_Retain || A == S_CanRelease) &&
|
|
(B == S_CanRelease || B == S_Use))
|
|
return B;
|
|
} else {
|
|
// Choose the side which is further along in the sequence.
|
|
if ((A == S_Use || A == S_CanRelease) &&
|
|
(B == S_Use || B == S_Release || B == S_Stop || B == S_MovableRelease))
|
|
return A;
|
|
// If both sides are releases, choose the more conservative one.
|
|
if (A == S_Stop && (B == S_Release || B == S_MovableRelease))
|
|
return A;
|
|
if (A == S_Release && B == S_MovableRelease)
|
|
return A;
|
|
}
|
|
|
|
return S_None;
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// RRInfo
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
void RRInfo::clear() {
|
|
KnownSafe = false;
|
|
IsTailCallRelease = false;
|
|
ReleaseMetadata = nullptr;
|
|
Calls.clear();
|
|
ReverseInsertPts.clear();
|
|
CFGHazardAfflicted = false;
|
|
}
|
|
|
|
bool RRInfo::Merge(const RRInfo &Other) {
|
|
// Conservatively merge the ReleaseMetadata information.
|
|
if (ReleaseMetadata != Other.ReleaseMetadata)
|
|
ReleaseMetadata = nullptr;
|
|
|
|
// Conservatively merge the boolean state.
|
|
KnownSafe &= Other.KnownSafe;
|
|
IsTailCallRelease &= Other.IsTailCallRelease;
|
|
CFGHazardAfflicted |= Other.CFGHazardAfflicted;
|
|
|
|
// Merge the call sets.
|
|
Calls.insert(Other.Calls.begin(), Other.Calls.end());
|
|
|
|
// Merge the insert point sets. If there are any differences,
|
|
// that makes this a partial merge.
|
|
bool Partial = ReverseInsertPts.size() != Other.ReverseInsertPts.size();
|
|
for (Instruction *Inst : Other.ReverseInsertPts)
|
|
Partial |= ReverseInsertPts.insert(Inst).second;
|
|
return Partial;
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// PtrState
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
void PtrState::SetKnownPositiveRefCount() {
|
|
LLVM_DEBUG(dbgs() << " Setting Known Positive.\n");
|
|
KnownPositiveRefCount = true;
|
|
}
|
|
|
|
void PtrState::ClearKnownPositiveRefCount() {
|
|
LLVM_DEBUG(dbgs() << " Clearing Known Positive.\n");
|
|
KnownPositiveRefCount = false;
|
|
}
|
|
|
|
void PtrState::SetSeq(Sequence NewSeq) {
|
|
LLVM_DEBUG(dbgs() << " Old: " << GetSeq() << "; New: " << NewSeq
|
|
<< "\n");
|
|
Seq = NewSeq;
|
|
}
|
|
|
|
void PtrState::ResetSequenceProgress(Sequence NewSeq) {
|
|
LLVM_DEBUG(dbgs() << " Resetting sequence progress.\n");
|
|
SetSeq(NewSeq);
|
|
Partial = false;
|
|
RRI.clear();
|
|
}
|
|
|
|
void PtrState::Merge(const PtrState &Other, bool TopDown) {
|
|
Seq = MergeSeqs(GetSeq(), Other.GetSeq(), TopDown);
|
|
KnownPositiveRefCount &= Other.KnownPositiveRefCount;
|
|
|
|
// If we're not in a sequence (anymore), drop all associated state.
|
|
if (Seq == S_None) {
|
|
Partial = false;
|
|
RRI.clear();
|
|
} else if (Partial || Other.Partial) {
|
|
// If we're doing a merge on a path that's previously seen a partial
|
|
// merge, conservatively drop the sequence, to avoid doing partial
|
|
// RR elimination. If the branch predicates for the two merge differ,
|
|
// mixing them is unsafe.
|
|
ClearSequenceProgress();
|
|
} else {
|
|
// Otherwise merge the other PtrState's RRInfo into our RRInfo. At this
|
|
// point, we know that currently we are not partial. Stash whether or not
|
|
// the merge operation caused us to undergo a partial merging of reverse
|
|
// insertion points.
|
|
Partial = RRI.Merge(Other.RRI);
|
|
}
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// BottomUpPtrState
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
bool BottomUpPtrState::InitBottomUp(ARCMDKindCache &Cache, Instruction *I) {
|
|
// If we see two releases in a row on the same pointer. If so, make
|
|
// a note, and we'll cicle back to revisit it after we've
|
|
// hopefully eliminated the second release, which may allow us to
|
|
// eliminate the first release too.
|
|
// Theoretically we could implement removal of nested retain+release
|
|
// pairs by making PtrState hold a stack of states, but this is
|
|
// simple and avoids adding overhead for the non-nested case.
|
|
bool NestingDetected = false;
|
|
if (GetSeq() == S_Release || GetSeq() == S_MovableRelease) {
|
|
LLVM_DEBUG(
|
|
dbgs() << " Found nested releases (i.e. a release pair)\n");
|
|
NestingDetected = true;
|
|
}
|
|
|
|
MDNode *ReleaseMetadata =
|
|
I->getMetadata(Cache.get(ARCMDKindID::ImpreciseRelease));
|
|
Sequence NewSeq = ReleaseMetadata ? S_MovableRelease : S_Release;
|
|
ResetSequenceProgress(NewSeq);
|
|
SetReleaseMetadata(ReleaseMetadata);
|
|
SetKnownSafe(HasKnownPositiveRefCount());
|
|
SetTailCallRelease(cast<CallInst>(I)->isTailCall());
|
|
InsertCall(I);
|
|
SetKnownPositiveRefCount();
|
|
return NestingDetected;
|
|
}
|
|
|
|
bool BottomUpPtrState::MatchWithRetain() {
|
|
SetKnownPositiveRefCount();
|
|
|
|
Sequence OldSeq = GetSeq();
|
|
switch (OldSeq) {
|
|
case S_Stop:
|
|
case S_Release:
|
|
case S_MovableRelease:
|
|
case S_Use:
|
|
// If OldSeq is not S_Use or OldSeq is S_Use and we are tracking an
|
|
// imprecise release, clear our reverse insertion points.
|
|
if (OldSeq != S_Use || IsTrackingImpreciseReleases())
|
|
ClearReverseInsertPts();
|
|
LLVM_FALLTHROUGH;
|
|
case S_CanRelease:
|
|
return true;
|
|
case S_None:
|
|
return false;
|
|
case S_Retain:
|
|
llvm_unreachable("bottom-up pointer in retain state!");
|
|
}
|
|
llvm_unreachable("Sequence unknown enum value");
|
|
}
|
|
|
|
bool BottomUpPtrState::HandlePotentialAlterRefCount(Instruction *Inst,
|
|
const Value *Ptr,
|
|
ProvenanceAnalysis &PA,
|
|
ARCInstKind Class) {
|
|
Sequence S = GetSeq();
|
|
|
|
// Check for possible releases.
|
|
if (!CanAlterRefCount(Inst, Ptr, PA, Class))
|
|
return false;
|
|
|
|
LLVM_DEBUG(dbgs() << " CanAlterRefCount: Seq: " << S << "; "
|
|
<< *Ptr << "\n");
|
|
switch (S) {
|
|
case S_Use:
|
|
SetSeq(S_CanRelease);
|
|
return true;
|
|
case S_CanRelease:
|
|
case S_Release:
|
|
case S_MovableRelease:
|
|
case S_Stop:
|
|
case S_None:
|
|
return false;
|
|
case S_Retain:
|
|
llvm_unreachable("bottom-up pointer in retain state!");
|
|
}
|
|
llvm_unreachable("Sequence unknown enum value");
|
|
}
|
|
|
|
void BottomUpPtrState::HandlePotentialUse(BasicBlock *BB, Instruction *Inst,
|
|
const Value *Ptr,
|
|
ProvenanceAnalysis &PA,
|
|
ARCInstKind Class) {
|
|
auto SetSeqAndInsertReverseInsertPt = [&](Sequence NewSeq){
|
|
assert(!HasReverseInsertPts());
|
|
SetSeq(NewSeq);
|
|
// If this is an invoke instruction, we're scanning it as part of
|
|
// one of its successor blocks, since we can't insert code after it
|
|
// in its own block, and we don't want to split critical edges.
|
|
BasicBlock::iterator InsertAfter;
|
|
if (isa<InvokeInst>(Inst)) {
|
|
const auto IP = BB->getFirstInsertionPt();
|
|
InsertAfter = IP == BB->end() ? std::prev(BB->end()) : IP;
|
|
if (isa<CatchSwitchInst>(InsertAfter))
|
|
// A catchswitch must be the only non-phi instruction in its basic
|
|
// block, so attempting to insert an instruction into such a block would
|
|
// produce invalid IR.
|
|
SetCFGHazardAfflicted(true);
|
|
} else {
|
|
InsertAfter = std::next(Inst->getIterator());
|
|
}
|
|
InsertReverseInsertPt(&*InsertAfter);
|
|
};
|
|
|
|
// Check for possible direct uses.
|
|
switch (GetSeq()) {
|
|
case S_Release:
|
|
case S_MovableRelease:
|
|
if (CanUse(Inst, Ptr, PA, Class)) {
|
|
LLVM_DEBUG(dbgs() << " CanUse: Seq: " << GetSeq() << "; "
|
|
<< *Ptr << "\n");
|
|
SetSeqAndInsertReverseInsertPt(S_Use);
|
|
} else if (Seq == S_Release && IsUser(Class)) {
|
|
LLVM_DEBUG(dbgs() << " PreciseReleaseUse: Seq: " << GetSeq()
|
|
<< "; " << *Ptr << "\n");
|
|
// Non-movable releases depend on any possible objc pointer use.
|
|
SetSeqAndInsertReverseInsertPt(S_Stop);
|
|
} else if (const auto *Call = getreturnRVOperand(*Inst, Class)) {
|
|
if (CanUse(Call, Ptr, PA, GetBasicARCInstKind(Call))) {
|
|
LLVM_DEBUG(dbgs() << " ReleaseUse: Seq: " << GetSeq() << "; "
|
|
<< *Ptr << "\n");
|
|
SetSeqAndInsertReverseInsertPt(S_Stop);
|
|
}
|
|
}
|
|
break;
|
|
case S_Stop:
|
|
if (CanUse(Inst, Ptr, PA, Class)) {
|
|
LLVM_DEBUG(dbgs() << " PreciseStopUse: Seq: " << GetSeq()
|
|
<< "; " << *Ptr << "\n");
|
|
SetSeq(S_Use);
|
|
}
|
|
break;
|
|
case S_CanRelease:
|
|
case S_Use:
|
|
case S_None:
|
|
break;
|
|
case S_Retain:
|
|
llvm_unreachable("bottom-up pointer in retain state!");
|
|
}
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// TopDownPtrState
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
bool TopDownPtrState::InitTopDown(ARCInstKind Kind, Instruction *I) {
|
|
bool NestingDetected = false;
|
|
// Don't do retain+release tracking for ARCInstKind::RetainRV, because
|
|
// it's
|
|
// better to let it remain as the first instruction after a call.
|
|
if (Kind != ARCInstKind::RetainRV) {
|
|
// If we see two retains in a row on the same pointer. If so, make
|
|
// a note, and we'll cicle back to revisit it after we've
|
|
// hopefully eliminated the second retain, which may allow us to
|
|
// eliminate the first retain too.
|
|
// Theoretically we could implement removal of nested retain+release
|
|
// pairs by making PtrState hold a stack of states, but this is
|
|
// simple and avoids adding overhead for the non-nested case.
|
|
if (GetSeq() == S_Retain)
|
|
NestingDetected = true;
|
|
|
|
ResetSequenceProgress(S_Retain);
|
|
SetKnownSafe(HasKnownPositiveRefCount());
|
|
InsertCall(I);
|
|
}
|
|
|
|
SetKnownPositiveRefCount();
|
|
return NestingDetected;
|
|
}
|
|
|
|
bool TopDownPtrState::MatchWithRelease(ARCMDKindCache &Cache,
|
|
Instruction *Release) {
|
|
ClearKnownPositiveRefCount();
|
|
|
|
Sequence OldSeq = GetSeq();
|
|
|
|
MDNode *ReleaseMetadata =
|
|
Release->getMetadata(Cache.get(ARCMDKindID::ImpreciseRelease));
|
|
|
|
switch (OldSeq) {
|
|
case S_Retain:
|
|
case S_CanRelease:
|
|
if (OldSeq == S_Retain || ReleaseMetadata != nullptr)
|
|
ClearReverseInsertPts();
|
|
LLVM_FALLTHROUGH;
|
|
case S_Use:
|
|
SetReleaseMetadata(ReleaseMetadata);
|
|
SetTailCallRelease(cast<CallInst>(Release)->isTailCall());
|
|
return true;
|
|
case S_None:
|
|
return false;
|
|
case S_Stop:
|
|
case S_Release:
|
|
case S_MovableRelease:
|
|
llvm_unreachable("top-down pointer in bottom up state!");
|
|
}
|
|
llvm_unreachable("Sequence unknown enum value");
|
|
}
|
|
|
|
bool TopDownPtrState::HandlePotentialAlterRefCount(Instruction *Inst,
|
|
const Value *Ptr,
|
|
ProvenanceAnalysis &PA,
|
|
ARCInstKind Class) {
|
|
// Check for possible releases. Treat clang.arc.use as a releasing instruction
|
|
// to prevent sinking a retain past it.
|
|
if (!CanAlterRefCount(Inst, Ptr, PA, Class) &&
|
|
Class != ARCInstKind::IntrinsicUser)
|
|
return false;
|
|
|
|
LLVM_DEBUG(dbgs() << " CanAlterRefCount: Seq: " << GetSeq() << "; "
|
|
<< *Ptr << "\n");
|
|
ClearKnownPositiveRefCount();
|
|
switch (GetSeq()) {
|
|
case S_Retain:
|
|
SetSeq(S_CanRelease);
|
|
assert(!HasReverseInsertPts());
|
|
InsertReverseInsertPt(Inst);
|
|
|
|
// One call can't cause a transition from S_Retain to S_CanRelease
|
|
// and S_CanRelease to S_Use. If we've made the first transition,
|
|
// we're done.
|
|
return true;
|
|
case S_Use:
|
|
case S_CanRelease:
|
|
case S_None:
|
|
return false;
|
|
case S_Stop:
|
|
case S_Release:
|
|
case S_MovableRelease:
|
|
llvm_unreachable("top-down pointer in release state!");
|
|
}
|
|
llvm_unreachable("covered switch is not covered!?");
|
|
}
|
|
|
|
void TopDownPtrState::HandlePotentialUse(Instruction *Inst, const Value *Ptr,
|
|
ProvenanceAnalysis &PA,
|
|
ARCInstKind Class) {
|
|
// Check for possible direct uses.
|
|
switch (GetSeq()) {
|
|
case S_CanRelease:
|
|
if (!CanUse(Inst, Ptr, PA, Class))
|
|
return;
|
|
LLVM_DEBUG(dbgs() << " CanUse: Seq: " << GetSeq() << "; "
|
|
<< *Ptr << "\n");
|
|
SetSeq(S_Use);
|
|
return;
|
|
case S_Retain:
|
|
case S_Use:
|
|
case S_None:
|
|
return;
|
|
case S_Stop:
|
|
case S_Release:
|
|
case S_MovableRelease:
|
|
llvm_unreachable("top-down pointer in release state!");
|
|
}
|
|
}
|