blob: f567cdc548ba7cb38aaa7e9fbfb6935060704ef8 [file] [edit]
//===- LiveOrigins.cpp - Live Origins Analysis -----------------*- C++ -*-===//
//
// 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 "clang/Analysis/Analyses/LifetimeSafety/LiveOrigins.h"
#include "Dataflow.h"
#include "llvm/Support/ErrorHandling.h"
namespace clang::lifetimes::internal {
namespace {
/// The dataflow lattice for origin liveness analysis.
/// It tracks which origins are live, why they're live (which UseFact),
/// and the confidence level of that liveness.
struct Lattice {
LivenessMap LiveOrigins;
Lattice() : LiveOrigins(nullptr) {};
explicit Lattice(LivenessMap L) : LiveOrigins(L) {}
bool operator==(const Lattice &Other) const {
return LiveOrigins == Other.LiveOrigins;
}
bool operator!=(const Lattice &Other) const { return !(*this == Other); }
void dump(llvm::raw_ostream &OS, const OriginManager &OM) const {
if (LiveOrigins.isEmpty())
OS << " <empty>\n";
for (const auto &Entry : LiveOrigins) {
OriginID OID = Entry.first;
const LivenessInfo &Info = Entry.second;
OS << " ";
OM.dump(OID, OS);
OS << " is ";
switch (Info.Kind) {
case LivenessKind::Must:
OS << "definitely";
break;
case LivenessKind::Maybe:
OS << "maybe";
break;
case LivenessKind::Dead:
llvm_unreachable("liveness kind of live origins should not be dead.");
}
OS << " live at this point\n";
}
}
};
static SourceLocation GetFactLoc(CausingFactType F) {
if (const auto *UF = F.dyn_cast<const UseFact *>())
return UF->getUseExpr()->getExprLoc();
if (const auto *OEF = F.dyn_cast<const OriginEscapesFact *>())
return OEF->getEscapeExpr()->getExprLoc();
llvm_unreachable("unhandled causing fact in PointerUnion");
}
/// The analysis that tracks which origins are live, with granular information
/// about the causing use fact and confidence level. This is a backward
/// analysis.
class AnalysisImpl
: public DataflowAnalysis<AnalysisImpl, Lattice, Direction::Backward> {
public:
AnalysisImpl(const CFG &C, AnalysisDeclContext &AC, FactManager &F,
LivenessMap::Factory &SF)
: DataflowAnalysis(C, AC, F), FactMgr(F), Factory(SF) {}
using DataflowAnalysis<AnalysisImpl, Lattice, Direction::Backward>::transfer;
StringRef getAnalysisName() const { return "LiveOrigins"; }
Lattice getInitialState() { return Lattice(Factory.getEmptyMap()); }
/// Merges two lattices by combining liveness information.
/// When the same origin has different confidence levels, we take the lower
/// one.
Lattice join(Lattice L1, Lattice L2) const {
LivenessMap Merged = L1.LiveOrigins;
// Take the earliest Fact to make the join hermetic and commutative.
auto CombineCausingFact = [](CausingFactType A,
CausingFactType B) -> CausingFactType {
if (!A)
return B;
if (!B)
return A;
return GetFactLoc(A) < GetFactLoc(B) ? A : B;
};
auto CombineLivenessKind = [](LivenessKind K1,
LivenessKind K2) -> LivenessKind {
assert(K1 != LivenessKind::Dead && "LivenessKind should not be dead.");
assert(K2 != LivenessKind::Dead && "LivenessKind should not be dead.");
// Only return "Must" if both paths are "Must", otherwise Maybe.
if (K1 == LivenessKind::Must && K2 == LivenessKind::Must)
return LivenessKind::Must;
return LivenessKind::Maybe;
};
auto CombineLivenessInfo = [&](const LivenessInfo *L1,
const LivenessInfo *L2) -> LivenessInfo {
assert((L1 || L2) && "unexpectedly merging 2 empty sets");
if (!L1)
return LivenessInfo(L2->CausingFact, LivenessKind::Maybe);
if (!L2)
return LivenessInfo(L1->CausingFact, LivenessKind::Maybe);
return LivenessInfo(CombineCausingFact(L1->CausingFact, L2->CausingFact),
CombineLivenessKind(L1->Kind, L2->Kind));
};
return Lattice(utils::join(
L1.LiveOrigins, L2.LiveOrigins, Factory, CombineLivenessInfo,
// A symmetric join is required here. If an origin is live on one
// branch but not the other, its confidence must be demoted to `Maybe`.
utils::JoinKind::Symmetric));
}
/// A read operation makes the origin live with definite confidence, as it
/// dominates this program point. A write operation kills the liveness of
/// the origin since it overwrites the value.
Lattice transfer(Lattice In, const UseFact &UF) {
Lattice Out = In;
for (const OriginList *Cur = UF.getUsedOrigins(); Cur;
Cur = Cur->peelOuterOrigin()) {
OriginID OID = Cur->getOuterOriginID();
// Write kills liveness.
if (UF.isWritten()) {
Out = Lattice(Factory.remove(Out.LiveOrigins, OID));
} else {
// Read makes origin live with definite confidence (dominates this
// point).
Out = Lattice(Factory.add(Out.LiveOrigins, OID,
LivenessInfo(&UF, LivenessKind::Must)));
}
}
return Out;
}
/// An escaping origin (e.g., via return) makes the origin live with definite
/// confidence, as it dominates this program point.
Lattice transfer(Lattice In, const OriginEscapesFact &OEF) {
OriginID OID = OEF.getEscapedOriginID();
return Lattice(Factory.add(In.LiveOrigins, OID,
LivenessInfo(&OEF, LivenessKind::Must)));
}
/// Issuing a new loan to an origin kills its liveness.
Lattice transfer(Lattice In, const IssueFact &IF) {
return Lattice(Factory.remove(In.LiveOrigins, IF.getOriginID()));
}
/// An OriginFlow kills the liveness of the destination origin if `KillDest`
/// is true. Otherwise, it propagates liveness from destination to source.
Lattice transfer(Lattice In, const OriginFlowFact &OF) {
if (!OF.getKillDest())
return In;
return Lattice(Factory.remove(In.LiveOrigins, OF.getDestOriginID()));
}
LivenessMap getLiveOriginsAt(ProgramPoint P) const {
return getState(P).LiveOrigins;
}
// Dump liveness values on all test points in the program.
void dump(llvm::raw_ostream &OS,
llvm::StringMap<ProgramPoint> TestPoints) const {
llvm::dbgs() << "==========================================\n";
llvm::dbgs() << getAnalysisName() << " results:\n";
llvm::dbgs() << "==========================================\n";
for (const auto &Entry : TestPoints) {
OS << "TestPoint: " << Entry.getKey() << "\n";
getState(Entry.getValue()).dump(OS, FactMgr.getOriginMgr());
}
}
private:
FactManager &FactMgr;
LivenessMap::Factory &Factory;
};
} // namespace
// PImpl wrapper implementation
class LiveOriginsAnalysis::Impl : public AnalysisImpl {
using AnalysisImpl::AnalysisImpl;
};
LiveOriginsAnalysis::LiveOriginsAnalysis(const CFG &C, AnalysisDeclContext &AC,
FactManager &F,
LivenessMap::Factory &SF)
: PImpl(std::make_unique<Impl>(C, AC, F, SF)) {
PImpl->run();
}
LiveOriginsAnalysis::~LiveOriginsAnalysis() = default;
LivenessMap LiveOriginsAnalysis::getLiveOriginsAt(ProgramPoint P) const {
return PImpl->getLiveOriginsAt(P);
}
void LiveOriginsAnalysis::dump(llvm::raw_ostream &OS,
llvm::StringMap<ProgramPoint> TestPoints) const {
PImpl->dump(OS, TestPoints);
}
} // namespace clang::lifetimes::internal