|
| 1 | +#ifndef LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_DATAFLOW_H |
| 2 | +#define LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_DATAFLOW_H |
| 3 | + |
| 4 | +#include "clang/Analysis/Analyses/LifetimeSafety/Facts.h" |
| 5 | +#include "clang/Analysis/AnalysisDeclContext.h" |
| 6 | +#include "clang/Analysis/CFG.h" |
| 7 | +#include "clang/Analysis/FlowSensitive/DataflowWorklist.h" |
| 8 | +#include "llvm/Support/Debug.h" |
| 9 | +#include "llvm/Support/ErrorHandling.h" |
| 10 | +#include "llvm/Support/TimeProfiler.h" |
| 11 | +#include <optional> |
| 12 | + |
| 13 | +namespace clang::lifetimes { |
| 14 | +namespace internal { |
| 15 | + |
| 16 | +// ========================================================================= // |
| 17 | +// Generic Dataflow Analysis |
| 18 | +// ========================================================================= // |
| 19 | + |
| 20 | +enum class Direction { Forward, Backward }; |
| 21 | + |
| 22 | +/// A `ProgramPoint` identifies a location in the CFG by pointing to a specific |
| 23 | +/// `Fact`. identified by a lifetime-related event (`Fact`). |
| 24 | +/// |
| 25 | +/// A `ProgramPoint` has "after" semantics: it represents the location |
| 26 | +/// immediately after its corresponding `Fact`. |
| 27 | +using ProgramPoint = const Fact *; |
| 28 | + |
| 29 | +/// A generic, policy-based driver for dataflow analyses. It combines |
| 30 | +/// the dataflow runner and the transferer logic into a single class hierarchy. |
| 31 | +/// |
| 32 | +/// The derived class is expected to provide: |
| 33 | +/// - A `Lattice` type. |
| 34 | +/// - `StringRef getAnalysisName() const` |
| 35 | +/// - `Lattice getInitialState();` The initial state of the analysis. |
| 36 | +/// - `Lattice join(Lattice, Lattice);` Merges states from multiple CFG paths. |
| 37 | +/// - `Lattice transfer(Lattice, const FactType&);` Defines how a single |
| 38 | +/// lifetime-relevant `Fact` transforms the lattice state. Only overloads |
| 39 | +/// for facts relevant to the analysis need to be implemented. |
| 40 | +/// |
| 41 | +/// \tparam Derived The CRTP derived class that implements the specific |
| 42 | +/// analysis. |
| 43 | +/// \tparam LatticeType The dataflow lattice used by the analysis. |
| 44 | +/// \tparam Dir The direction of the analysis (Forward or Backward). |
| 45 | +/// TODO: Maybe use the dataflow framework! The framework might need changes |
| 46 | +/// to support the current comparison done at block-entry. |
| 47 | +template <typename Derived, typename LatticeType, Direction Dir> |
| 48 | +class DataflowAnalysis { |
| 49 | +public: |
| 50 | + using Lattice = LatticeType; |
| 51 | + using Base = DataflowAnalysis<Derived, Lattice, Dir>; |
| 52 | + |
| 53 | +private: |
| 54 | + const CFG &Cfg; |
| 55 | + AnalysisDeclContext &AC; |
| 56 | + |
| 57 | + /// The dataflow state before a basic block is processed. |
| 58 | + llvm::DenseMap<const CFGBlock *, Lattice> InStates; |
| 59 | + /// The dataflow state after a basic block is processed. |
| 60 | + llvm::DenseMap<const CFGBlock *, Lattice> OutStates; |
| 61 | + /// The dataflow state at a Program Point. |
| 62 | + /// In a forward analysis, this is the state after the Fact at that point has |
| 63 | + /// been applied, while in a backward analysis, it is the state before. |
| 64 | + llvm::DenseMap<ProgramPoint, Lattice> PerPointStates; |
| 65 | + |
| 66 | + static constexpr bool isForward() { return Dir == Direction::Forward; } |
| 67 | + |
| 68 | +protected: |
| 69 | + FactManager &AllFacts; |
| 70 | + |
| 71 | + explicit DataflowAnalysis(const CFG &C, AnalysisDeclContext &AC, |
| 72 | + FactManager &F) |
| 73 | + : Cfg(C), AC(AC), AllFacts(F) {} |
| 74 | + |
| 75 | +public: |
| 76 | + void run() { |
| 77 | + Derived &D = static_cast<Derived &>(*this); |
| 78 | + llvm::TimeTraceScope Time(D.getAnalysisName()); |
| 79 | + |
| 80 | + using Worklist = |
| 81 | + std::conditional_t<Dir == Direction::Forward, ForwardDataflowWorklist, |
| 82 | + BackwardDataflowWorklist>; |
| 83 | + Worklist W(Cfg, AC); |
| 84 | + |
| 85 | + const CFGBlock *Start = isForward() ? &Cfg.getEntry() : &Cfg.getExit(); |
| 86 | + InStates[Start] = D.getInitialState(); |
| 87 | + W.enqueueBlock(Start); |
| 88 | + |
| 89 | + while (const CFGBlock *B = W.dequeue()) { |
| 90 | + Lattice StateIn = *getInState(B); |
| 91 | + Lattice StateOut = transferBlock(B, StateIn); |
| 92 | + OutStates[B] = StateOut; |
| 93 | + for (const CFGBlock *AdjacentB : isForward() ? B->succs() : B->preds()) { |
| 94 | + if (!AdjacentB) |
| 95 | + continue; |
| 96 | + std::optional<Lattice> OldInState = getInState(AdjacentB); |
| 97 | + Lattice NewInState = |
| 98 | + !OldInState ? StateOut : D.join(*OldInState, StateOut); |
| 99 | + // Enqueue the adjacent block if its in-state has changed or if we have |
| 100 | + // never seen it. |
| 101 | + if (!OldInState || NewInState != *OldInState) { |
| 102 | + InStates[AdjacentB] = NewInState; |
| 103 | + W.enqueueBlock(AdjacentB); |
| 104 | + } |
| 105 | + } |
| 106 | + } |
| 107 | + } |
| 108 | + |
| 109 | +protected: |
| 110 | + Lattice getState(ProgramPoint P) const { return PerPointStates.lookup(P); } |
| 111 | + |
| 112 | + std::optional<Lattice> getInState(const CFGBlock *B) const { |
| 113 | + auto It = InStates.find(B); |
| 114 | + if (It == InStates.end()) |
| 115 | + return std::nullopt; |
| 116 | + return It->second; |
| 117 | + } |
| 118 | + |
| 119 | + Lattice getOutState(const CFGBlock *B) const { return OutStates.lookup(B); } |
| 120 | + |
| 121 | + void dump() const { |
| 122 | + const Derived *D = static_cast<const Derived *>(this); |
| 123 | + llvm::dbgs() << "==========================================\n"; |
| 124 | + llvm::dbgs() << D->getAnalysisName() << " results:\n"; |
| 125 | + llvm::dbgs() << "==========================================\n"; |
| 126 | + const CFGBlock &B = isForward() ? Cfg.getExit() : Cfg.getEntry(); |
| 127 | + getOutState(&B).dump(llvm::dbgs()); |
| 128 | + } |
| 129 | + |
| 130 | +private: |
| 131 | + /// Computes the state at one end of a block by applying all its facts |
| 132 | + /// sequentially to a given state from the other end. |
| 133 | + Lattice transferBlock(const CFGBlock *Block, Lattice State) { |
| 134 | + auto Facts = AllFacts.getFacts(Block); |
| 135 | + if constexpr (isForward()) { |
| 136 | + for (const Fact *F : Facts) { |
| 137 | + State = transferFact(State, F); |
| 138 | + PerPointStates[F] = State; |
| 139 | + } |
| 140 | + } else { |
| 141 | + for (const Fact *F : llvm::reverse(Facts)) { |
| 142 | + // In backward analysis, capture the state before applying the fact. |
| 143 | + PerPointStates[F] = State; |
| 144 | + State = transferFact(State, F); |
| 145 | + } |
| 146 | + } |
| 147 | + return State; |
| 148 | + } |
| 149 | + |
| 150 | + Lattice transferFact(Lattice In, const Fact *F) { |
| 151 | + assert(F); |
| 152 | + Derived *D = static_cast<Derived *>(this); |
| 153 | + switch (F->getKind()) { |
| 154 | + case Fact::Kind::Issue: |
| 155 | + return D->transfer(In, *F->getAs<IssueFact>()); |
| 156 | + case Fact::Kind::Expire: |
| 157 | + return D->transfer(In, *F->getAs<ExpireFact>()); |
| 158 | + case Fact::Kind::OriginFlow: |
| 159 | + return D->transfer(In, *F->getAs<OriginFlowFact>()); |
| 160 | + case Fact::Kind::ReturnOfOrigin: |
| 161 | + return D->transfer(In, *F->getAs<ReturnOfOriginFact>()); |
| 162 | + case Fact::Kind::Use: |
| 163 | + return D->transfer(In, *F->getAs<UseFact>()); |
| 164 | + case Fact::Kind::TestPoint: |
| 165 | + return D->transfer(In, *F->getAs<TestPointFact>()); |
| 166 | + } |
| 167 | + llvm_unreachable("Unknown fact kind"); |
| 168 | + } |
| 169 | + |
| 170 | +public: |
| 171 | + Lattice transfer(Lattice In, const IssueFact &) { return In; } |
| 172 | + Lattice transfer(Lattice In, const ExpireFact &) { return In; } |
| 173 | + Lattice transfer(Lattice In, const OriginFlowFact &) { return In; } |
| 174 | + Lattice transfer(Lattice In, const ReturnOfOriginFact &) { return In; } |
| 175 | + Lattice transfer(Lattice In, const UseFact &) { return In; } |
| 176 | + Lattice transfer(Lattice In, const TestPointFact &) { return In; } |
| 177 | +}; |
| 178 | +} // namespace internal |
| 179 | +} // namespace clang::lifetimes |
| 180 | +#endif // LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_DATAFLOW_H |
0 commit comments