| //===- 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 |