Details | Last modification | View Log | RSS feed
| Rev | Author | Line No. | Line |
|---|---|---|---|
| 14 | pmbaty | 1 | //===- DataflowAnalysis.h ---------------------------------------*- C++ -*-===// |
| 2 | // |
||
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
||
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
||
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
||
| 6 | // |
||
| 7 | //===----------------------------------------------------------------------===// |
||
| 8 | // |
||
| 9 | // This file defines base types and functions for building dataflow analyses |
||
| 10 | // that run over Control-Flow Graphs (CFGs). |
||
| 11 | // |
||
| 12 | //===----------------------------------------------------------------------===// |
||
| 13 | |||
| 14 | #ifndef LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_DATAFLOWANALYSIS_H |
||
| 15 | #define LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_DATAFLOWANALYSIS_H |
||
| 16 | |||
| 17 | #include <iterator> |
||
| 18 | #include <optional> |
||
| 19 | #include <type_traits> |
||
| 20 | #include <utility> |
||
| 21 | #include <vector> |
||
| 22 | |||
| 23 | #include "clang/AST/ASTContext.h" |
||
| 24 | #include "clang/Analysis/CFG.h" |
||
| 25 | #include "clang/Analysis/FlowSensitive/ControlFlowContext.h" |
||
| 26 | #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h" |
||
| 27 | #include "clang/Analysis/FlowSensitive/DataflowLattice.h" |
||
| 28 | #include "clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h" |
||
| 29 | #include "llvm/ADT/Any.h" |
||
| 30 | #include "llvm/ADT/STLExtras.h" |
||
| 31 | #include "llvm/Support/Error.h" |
||
| 32 | |||
| 33 | namespace clang { |
||
| 34 | namespace dataflow { |
||
| 35 | |||
| 36 | /// Base class template for dataflow analyses built on a single lattice type. |
||
| 37 | /// |
||
| 38 | /// Requirements: |
||
| 39 | /// |
||
| 40 | /// `Derived` must be derived from a specialization of this class template and |
||
| 41 | /// must provide the following public members: |
||
| 42 | /// * `LatticeT initialElement()` - returns a lattice element that models the |
||
| 43 | /// initial state of a basic block; |
||
| 44 | /// * `void transfer(const CFGElement *, LatticeT &, Environment &)` - applies |
||
| 45 | /// the analysis transfer function for a given CFG element and lattice |
||
| 46 | /// element. |
||
| 47 | /// |
||
| 48 | /// `Derived` can optionally provide the following members: |
||
| 49 | /// * `void transferBranch(bool Branch, const Stmt *Stmt, TypeErasedLattice &E, |
||
| 50 | /// Environment &Env)` - applies the analysis transfer |
||
| 51 | /// function for a given edge from a CFG block of a conditional statement. |
||
| 52 | /// |
||
| 53 | /// `Derived` can optionally override the following members: |
||
| 54 | /// * `bool merge(QualType, const Value &, const Value &, Value &, |
||
| 55 | /// Environment &)` - joins distinct values. This could be a strict |
||
| 56 | /// lattice join or a more general widening operation. |
||
| 57 | /// |
||
| 58 | /// `LatticeT` is a bounded join-semilattice that is used by `Derived` and must |
||
| 59 | /// provide the following public members: |
||
| 60 | /// * `LatticeJoinEffect join(const LatticeT &)` - joins the object and the |
||
| 61 | /// argument by computing their least upper bound, modifies the object if |
||
| 62 | /// necessary, and returns an effect indicating whether any changes were |
||
| 63 | /// made to it; |
||
| 64 | /// * `bool operator==(const LatticeT &) const` - returns true if and only if |
||
| 65 | /// the object is equal to the argument. |
||
| 66 | /// |
||
| 67 | /// `LatticeT` can optionally provide the following members: |
||
| 68 | /// * `LatticeJoinEffect widen(const LatticeT &Previous)` - replaces the |
||
| 69 | /// lattice element with an approximation that can reach a fixed point more |
||
| 70 | /// quickly than iterated application of the transfer function alone. The |
||
| 71 | /// previous value is provided to inform the choice of widened value. The |
||
| 72 | /// function must also serve as a comparison operation, by indicating whether |
||
| 73 | /// the widened value is equivalent to the previous value with the returned |
||
| 74 | /// `LatticeJoinEffect`. |
||
| 75 | template <typename Derived, typename LatticeT> |
||
| 76 | class DataflowAnalysis : public TypeErasedDataflowAnalysis { |
||
| 77 | public: |
||
| 78 | /// Bounded join-semilattice that is used in the analysis. |
||
| 79 | using Lattice = LatticeT; |
||
| 80 | |||
| 81 | explicit DataflowAnalysis(ASTContext &Context) : Context(Context) {} |
||
| 82 | |||
| 83 | /// Deprecated. Use the `DataflowAnalysisOptions` constructor instead. |
||
| 84 | explicit DataflowAnalysis(ASTContext &Context, bool ApplyBuiltinTransfer) |
||
| 85 | : DataflowAnalysis( |
||
| 86 | Context, |
||
| 87 | {ApplyBuiltinTransfer |
||
| 88 | ? DataflowAnalysisContext::Options{} |
||
| 89 | : std::optional<DataflowAnalysisContext::Options>()}) {} |
||
| 90 | |||
| 91 | explicit DataflowAnalysis(ASTContext &Context, |
||
| 92 | DataflowAnalysisOptions Options) |
||
| 93 | : TypeErasedDataflowAnalysis(Options), Context(Context) {} |
||
| 94 | |||
| 95 | ASTContext &getASTContext() final { return Context; } |
||
| 96 | |||
| 97 | TypeErasedLattice typeErasedInitialElement() final { |
||
| 98 | return {static_cast<Derived *>(this)->initialElement()}; |
||
| 99 | } |
||
| 100 | |||
| 101 | LatticeJoinEffect joinTypeErased(TypeErasedLattice &E1, |
||
| 102 | const TypeErasedLattice &E2) final { |
||
| 103 | Lattice &L1 = llvm::any_cast<Lattice &>(E1.Value); |
||
| 104 | const Lattice &L2 = llvm::any_cast<const Lattice &>(E2.Value); |
||
| 105 | return L1.join(L2); |
||
| 106 | } |
||
| 107 | |||
| 108 | LatticeJoinEffect widenTypeErased(TypeErasedLattice &Current, |
||
| 109 | const TypeErasedLattice &Previous) final { |
||
| 110 | Lattice &C = llvm::any_cast<Lattice &>(Current.Value); |
||
| 111 | const Lattice &P = llvm::any_cast<const Lattice &>(Previous.Value); |
||
| 112 | return widenInternal(Rank0{}, C, P); |
||
| 113 | } |
||
| 114 | |||
| 115 | bool isEqualTypeErased(const TypeErasedLattice &E1, |
||
| 116 | const TypeErasedLattice &E2) final { |
||
| 117 | const Lattice &L1 = llvm::any_cast<const Lattice &>(E1.Value); |
||
| 118 | const Lattice &L2 = llvm::any_cast<const Lattice &>(E2.Value); |
||
| 119 | return L1 == L2; |
||
| 120 | } |
||
| 121 | |||
| 122 | void transferTypeErased(const CFGElement *Element, TypeErasedLattice &E, |
||
| 123 | Environment &Env) final { |
||
| 124 | Lattice &L = llvm::any_cast<Lattice &>(E.Value); |
||
| 125 | static_cast<Derived *>(this)->transfer(Element, L, Env); |
||
| 126 | } |
||
| 127 | |||
| 128 | void transferBranchTypeErased(bool Branch, const Stmt *Stmt, |
||
| 129 | TypeErasedLattice &E, Environment &Env) final { |
||
| 130 | transferBranchInternal(Rank0{}, *static_cast<Derived *>(this), Branch, Stmt, |
||
| 131 | E, Env); |
||
| 132 | } |
||
| 133 | |||
| 134 | private: |
||
| 135 | // These `Rank` structs are used for template metaprogramming to choose |
||
| 136 | // between overloads. |
||
| 137 | struct Rank1 {}; |
||
| 138 | struct Rank0 : Rank1 {}; |
||
| 139 | |||
| 140 | // The first-choice implementation: use `widen` when it is available. |
||
| 141 | template <typename T> |
||
| 142 | static auto widenInternal(Rank0, T &Current, const T &Prev) |
||
| 143 | -> decltype(Current.widen(Prev)) { |
||
| 144 | return Current.widen(Prev); |
||
| 145 | } |
||
| 146 | |||
| 147 | // The second-choice implementation: `widen` is unavailable. Widening is |
||
| 148 | // merged with equality checking, so when widening is unimplemented, we |
||
| 149 | // default to equality checking. |
||
| 150 | static LatticeJoinEffect widenInternal(Rank1, const Lattice &Current, |
||
| 151 | const Lattice &Prev) { |
||
| 152 | return Prev == Current ? LatticeJoinEffect::Unchanged |
||
| 153 | : LatticeJoinEffect::Changed; |
||
| 154 | } |
||
| 155 | |||
| 156 | // The first-choice implementation: `transferBranch` is implemented. |
||
| 157 | template <typename Analysis> |
||
| 158 | static auto transferBranchInternal(Rank0, Analysis &A, bool Branch, |
||
| 159 | const Stmt *Stmt, TypeErasedLattice &L, |
||
| 160 | Environment &Env) |
||
| 161 | -> std::void_t<decltype(A.transferBranch( |
||
| 162 | Branch, Stmt, std::declval<LatticeT &>(), Env))> { |
||
| 163 | A.transferBranch(Branch, Stmt, llvm::any_cast<Lattice &>(L.Value), Env); |
||
| 164 | } |
||
| 165 | |||
| 166 | // The second-choice implementation: `transferBranch` is unimplemented. No-op. |
||
| 167 | template <typename Analysis> |
||
| 168 | static void transferBranchInternal(Rank1, Analysis &A, bool, const Stmt *, |
||
| 169 | TypeErasedLattice &, Environment &) {} |
||
| 170 | |||
| 171 | ASTContext &Context; |
||
| 172 | }; |
||
| 173 | |||
| 174 | // Model of the program at a given program point. |
||
| 175 | template <typename LatticeT> struct DataflowAnalysisState { |
||
| 176 | // Model of a program property. |
||
| 177 | LatticeT Lattice; |
||
| 178 | |||
| 179 | // Model of the state of the program (store and heap). |
||
| 180 | Environment Env; |
||
| 181 | }; |
||
| 182 | |||
| 183 | /// Performs dataflow analysis and returns a mapping from basic block IDs to |
||
| 184 | /// dataflow analysis states that model the respective basic blocks. The |
||
| 185 | /// returned vector, if any, will have the same size as the number of CFG |
||
| 186 | /// blocks, with indices corresponding to basic block IDs. Returns an error if |
||
| 187 | /// the dataflow analysis cannot be performed successfully. Otherwise, calls |
||
| 188 | /// `PostVisitCFG` on each CFG element with the final analysis results at that |
||
| 189 | /// program point. |
||
| 190 | template <typename AnalysisT> |
||
| 191 | llvm::Expected<std::vector< |
||
| 192 | std::optional<DataflowAnalysisState<typename AnalysisT::Lattice>>>> |
||
| 193 | runDataflowAnalysis( |
||
| 194 | const ControlFlowContext &CFCtx, AnalysisT &Analysis, |
||
| 195 | const Environment &InitEnv, |
||
| 196 | std::function<void(const CFGElement &, const DataflowAnalysisState< |
||
| 197 | typename AnalysisT::Lattice> &)> |
||
| 198 | PostVisitCFG = nullptr) { |
||
| 199 | std::function<void(const CFGElement &, |
||
| 200 | const TypeErasedDataflowAnalysisState &)> |
||
| 201 | PostVisitCFGClosure = nullptr; |
||
| 202 | if (PostVisitCFG) { |
||
| 203 | PostVisitCFGClosure = [&PostVisitCFG]( |
||
| 204 | const CFGElement &Element, |
||
| 205 | const TypeErasedDataflowAnalysisState &State) { |
||
| 206 | auto *Lattice = |
||
| 207 | llvm::any_cast<typename AnalysisT::Lattice>(&State.Lattice.Value); |
||
| 208 | PostVisitCFG(Element, DataflowAnalysisState<typename AnalysisT::Lattice>{ |
||
| 209 | *Lattice, State.Env}); |
||
| 210 | }; |
||
| 211 | } |
||
| 212 | |||
| 213 | auto TypeErasedBlockStates = runTypeErasedDataflowAnalysis( |
||
| 214 | CFCtx, Analysis, InitEnv, PostVisitCFGClosure); |
||
| 215 | if (!TypeErasedBlockStates) |
||
| 216 | return TypeErasedBlockStates.takeError(); |
||
| 217 | |||
| 218 | std::vector<std::optional<DataflowAnalysisState<typename AnalysisT::Lattice>>> |
||
| 219 | BlockStates; |
||
| 220 | BlockStates.reserve(TypeErasedBlockStates->size()); |
||
| 221 | |||
| 222 | llvm::transform( |
||
| 223 | std::move(*TypeErasedBlockStates), std::back_inserter(BlockStates), |
||
| 224 | [](auto &OptState) { |
||
| 225 | return llvm::transformOptional(std::move(OptState), [](auto &&State) { |
||
| 226 | return DataflowAnalysisState<typename AnalysisT::Lattice>{ |
||
| 227 | llvm::any_cast<typename AnalysisT::Lattice>( |
||
| 228 | std::move(State.Lattice.Value)), |
||
| 229 | std::move(State.Env)}; |
||
| 230 | }); |
||
| 231 | }); |
||
| 232 | return BlockStates; |
||
| 233 | } |
||
| 234 | |||
| 235 | /// Abstract base class for dataflow "models": reusable analysis components that |
||
| 236 | /// model a particular aspect of program semantics in the `Environment`. For |
||
| 237 | /// example, a model may capture a type and its related functions. |
||
| 238 | class DataflowModel : public Environment::ValueModel { |
||
| 239 | public: |
||
| 240 | /// Return value indicates whether the model processed the `Element`. |
||
| 241 | virtual bool transfer(const CFGElement *Element, Environment &Env) = 0; |
||
| 242 | }; |
||
| 243 | |||
| 244 | } // namespace dataflow |
||
| 245 | } // namespace clang |
||
| 246 | |||
| 247 | #endif // LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_DATAFLOWANALYSIS_H |