123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513 |
- #pragma once
- #ifdef __GNUC__
- #pragma GCC diagnostic push
- #pragma GCC diagnostic ignored "-Wunused-parameter"
- #endif
- //===-- DataflowEnvironment.h -----------------------------------*- 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
- //
- //===----------------------------------------------------------------------===//
- //
- // This file defines an Environment class that is used by dataflow analyses
- // that run over Control-Flow Graphs (CFGs) to keep track of the state of the
- // program at given program points.
- //
- //===----------------------------------------------------------------------===//
- #ifndef LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_DATAFLOWENVIRONMENT_H
- #define LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_DATAFLOWENVIRONMENT_H
- #include "clang/AST/Decl.h"
- #include "clang/AST/DeclBase.h"
- #include "clang/AST/Expr.h"
- #include "clang/AST/Type.h"
- #include "clang/Analysis/FlowSensitive/ControlFlowContext.h"
- #include "clang/Analysis/FlowSensitive/DataflowAnalysisContext.h"
- #include "clang/Analysis/FlowSensitive/DataflowLattice.h"
- #include "clang/Analysis/FlowSensitive/StorageLocation.h"
- #include "clang/Analysis/FlowSensitive/Value.h"
- #include "llvm/ADT/DenseMap.h"
- #include "llvm/ADT/DenseSet.h"
- #include "llvm/Support/ErrorHandling.h"
- #include <memory>
- #include <type_traits>
- #include <utility>
- namespace clang {
- namespace dataflow {
- /// Indicates what kind of indirections should be skipped past when retrieving
- /// storage locations or values.
- ///
- /// FIXME: Consider renaming this or replacing it with a more appropriate model.
- /// See the discussion in https://reviews.llvm.org/D116596 for context.
- enum class SkipPast {
- /// No indirections should be skipped past.
- None,
- /// An optional reference should be skipped past.
- Reference,
- /// An optional reference should be skipped past, then an optional pointer
- /// should be skipped past.
- ReferenceThenPointer,
- };
- /// Indicates the result of a tentative comparison.
- enum class ComparisonResult {
- Same,
- Different,
- Unknown,
- };
- /// Holds the state of the program (store and heap) at a given program point.
- ///
- /// WARNING: Symbolic values that are created by the environment for static
- /// local and global variables are not currently invalidated on function calls.
- /// This is unsound and should be taken into account when designing dataflow
- /// analyses.
- class Environment {
- public:
- /// Supplements `Environment` with non-standard comparison and join
- /// operations.
- class ValueModel {
- public:
- virtual ~ValueModel() = default;
- /// Returns:
- /// `Same`: `Val1` is equivalent to `Val2`, according to the model.
- /// `Different`: `Val1` is distinct from `Val2`, according to the model.
- /// `Unknown`: The model can't determine a relationship between `Val1` and
- /// `Val2`.
- ///
- /// Requirements:
- ///
- /// `Val1` and `Val2` must be distinct.
- ///
- /// `Val1` and `Val2` must model values of type `Type`.
- ///
- /// `Val1` and `Val2` must be assigned to the same storage location in
- /// `Env1` and `Env2` respectively.
- virtual ComparisonResult compare(QualType Type, const Value &Val1,
- const Environment &Env1, const Value &Val2,
- const Environment &Env2) {
- // FIXME: Consider adding QualType to StructValue and removing the Type
- // argument here.
- return ComparisonResult::Unknown;
- }
- /// Modifies `MergedVal` to approximate both `Val1` and `Val2`. This could
- /// be a strict lattice join or a more general widening operation.
- ///
- /// If this function returns true, `MergedVal` will be assigned to a storage
- /// location of type `Type` in `MergedEnv`.
- ///
- /// `Env1` and `Env2` can be used to query child values and path condition
- /// implications of `Val1` and `Val2` respectively.
- ///
- /// Requirements:
- ///
- /// `Val1` and `Val2` must be distinct.
- ///
- /// `Val1`, `Val2`, and `MergedVal` must model values of type `Type`.
- ///
- /// `Val1` and `Val2` must be assigned to the same storage location in
- /// `Env1` and `Env2` respectively.
- virtual bool merge(QualType Type, const Value &Val1,
- const Environment &Env1, const Value &Val2,
- const Environment &Env2, Value &MergedVal,
- Environment &MergedEnv) {
- return true;
- }
- /// This function may widen the current value -- replace it with an
- /// approximation that can reach a fixed point more quickly than iterated
- /// application of the transfer function alone. The previous value is
- /// provided to inform the choice of widened value. The function must also
- /// serve as a comparison operation, by indicating whether the widened value
- /// is equivalent to the previous value.
- ///
- /// Returns either:
- ///
- /// `nullptr`, if this value is not of interest to the model, or
- ///
- /// `&Prev`, if the widened value is equivalent to `Prev`, or
- ///
- /// A non-null value that approximates `Current`. `Prev` is available to
- /// inform the chosen approximation.
- ///
- /// `PrevEnv` and `CurrentEnv` can be used to query child values and path
- /// condition implications of `Prev` and `Current`, respectively.
- ///
- /// Requirements:
- ///
- /// `Prev` and `Current` must model values of type `Type`.
- ///
- /// `Prev` and `Current` must be assigned to the same storage location in
- /// `PrevEnv` and `CurrentEnv`, respectively.
- virtual Value *widen(QualType Type, Value &Prev, const Environment &PrevEnv,
- Value &Current, Environment &CurrentEnv) {
- // The default implementation reduces to just comparison, since comparison
- // is required by the API, even if no widening is performed.
- switch (compare(Type, Prev, PrevEnv, Current, CurrentEnv)) {
- case ComparisonResult::Same:
- return &Prev;
- case ComparisonResult::Different:
- return &Current;
- case ComparisonResult::Unknown:
- return nullptr;
- }
- llvm_unreachable("all cases in switch covered");
- }
- };
- /// Creates an environment that uses `DACtx` to store objects that encompass
- /// the state of a program.
- explicit Environment(DataflowAnalysisContext &DACtx);
- Environment(const Environment &Other);
- Environment &operator=(const Environment &Other);
- Environment(Environment &&Other) = default;
- Environment &operator=(Environment &&Other) = default;
- /// Creates an environment that uses `DACtx` to store objects that encompass
- /// the state of a program.
- ///
- /// If `DeclCtx` is a function, initializes the environment with symbolic
- /// representations of the function parameters.
- ///
- /// If `DeclCtx` is a non-static member function, initializes the environment
- /// with a symbolic representation of the `this` pointee.
- Environment(DataflowAnalysisContext &DACtx, const DeclContext &DeclCtx);
- const DataflowAnalysisContext::Options &getAnalysisOptions() {
- return DACtx->getOptions();
- }
- /// Creates and returns an environment to use for an inline analysis of the
- /// callee. Uses the storage location from each argument in the `Call` as the
- /// storage location for the corresponding parameter in the callee.
- ///
- /// Requirements:
- ///
- /// The callee of `Call` must be a `FunctionDecl`.
- ///
- /// The body of the callee must not reference globals.
- ///
- /// The arguments of `Call` must map 1:1 to the callee's parameters.
- Environment pushCall(const CallExpr *Call) const;
- Environment pushCall(const CXXConstructExpr *Call) const;
- /// Moves gathered information back into `this` from a `CalleeEnv` created via
- /// `pushCall`.
- void popCall(const Environment &CalleeEnv);
- /// Returns true if and only if the environment is equivalent to `Other`, i.e
- /// the two environments:
- /// - have the same mappings from declarations to storage locations,
- /// - have the same mappings from expressions to storage locations,
- /// - have the same or equivalent (according to `Model`) values assigned to
- /// the same storage locations.
- ///
- /// Requirements:
- ///
- /// `Other` and `this` must use the same `DataflowAnalysisContext`.
- bool equivalentTo(const Environment &Other,
- Environment::ValueModel &Model) const;
- /// Joins the environment with `Other` by taking the intersection of storage
- /// locations and values that are stored in them. Distinct values that are
- /// assigned to the same storage locations in the environment and `Other` are
- /// merged using `Model`.
- ///
- /// Requirements:
- ///
- /// `Other` and `this` must use the same `DataflowAnalysisContext`.
- LatticeJoinEffect join(const Environment &Other,
- Environment::ValueModel &Model);
- /// Widens the environment point-wise, using `PrevEnv` as needed to inform the
- /// approximation.
- ///
- /// Requirements:
- ///
- /// `PrevEnv` must be the immediate previous version of the environment.
- /// `PrevEnv` and `this` must use the same `DataflowAnalysisContext`.
- LatticeJoinEffect widen(const Environment &PrevEnv,
- Environment::ValueModel &Model);
- // FIXME: Rename `createOrGetStorageLocation` to `getOrCreateStorageLocation`,
- // `getStableStorageLocation`, or something more appropriate.
- /// Creates a storage location appropriate for `Type`. Does not assign a value
- /// to the returned storage location in the environment.
- ///
- /// Requirements:
- ///
- /// `Type` must not be null.
- StorageLocation &createStorageLocation(QualType Type);
- /// Creates a storage location for `D`. Does not assign the returned storage
- /// location to `D` in the environment. Does not assign a value to the
- /// returned storage location in the environment.
- StorageLocation &createStorageLocation(const VarDecl &D);
- /// Creates a storage location for `E`. Does not assign the returned storage
- /// location to `E` in the environment. Does not assign a value to the
- /// returned storage location in the environment.
- StorageLocation &createStorageLocation(const Expr &E);
- /// Assigns `Loc` as the storage location of `D` in the environment.
- ///
- /// Requirements:
- ///
- /// `D` must not be assigned a storage location in the environment.
- void setStorageLocation(const ValueDecl &D, StorageLocation &Loc);
- /// Returns the storage location assigned to `D` in the environment, applying
- /// the `SP` policy for skipping past indirections, or null if `D` isn't
- /// assigned a storage location in the environment.
- StorageLocation *getStorageLocation(const ValueDecl &D, SkipPast SP) const;
- /// Assigns `Loc` as the storage location of `E` in the environment.
- ///
- /// Requirements:
- ///
- /// `E` must not be assigned a storage location in the environment.
- void setStorageLocation(const Expr &E, StorageLocation &Loc);
- /// Returns the storage location assigned to `E` in the environment, applying
- /// the `SP` policy for skipping past indirections, or null if `E` isn't
- /// assigned a storage location in the environment.
- StorageLocation *getStorageLocation(const Expr &E, SkipPast SP) const;
- /// Returns the storage location assigned to the `this` pointee in the
- /// environment or null if the `this` pointee has no assigned storage location
- /// in the environment.
- StorageLocation *getThisPointeeStorageLocation() const;
- /// Returns the storage location of the return value or null, if unset.
- StorageLocation *getReturnStorageLocation() const;
- /// Returns a pointer value that represents a null pointer. Calls with
- /// `PointeeType` that are canonically equivalent will return the same result.
- PointerValue &getOrCreateNullPointerValue(QualType PointeeType);
- /// Creates a value appropriate for `Type`, if `Type` is supported, otherwise
- /// return null. If `Type` is a pointer or reference type, creates all the
- /// necessary storage locations and values for indirections until it finds a
- /// non-pointer/non-reference type.
- ///
- /// Requirements:
- ///
- /// `Type` must not be null.
- Value *createValue(QualType Type);
- /// Assigns `Val` as the value of `Loc` in the environment.
- void setValue(const StorageLocation &Loc, Value &Val);
- /// Returns the value assigned to `Loc` in the environment or null if `Loc`
- /// isn't assigned a value in the environment.
- Value *getValue(const StorageLocation &Loc) const;
- /// Equivalent to `getValue(getStorageLocation(D, SP), SkipPast::None)` if `D`
- /// is assigned a storage location in the environment, otherwise returns null.
- Value *getValue(const ValueDecl &D, SkipPast SP) const;
- /// Equivalent to `getValue(getStorageLocation(E, SP), SkipPast::None)` if `E`
- /// is assigned a storage location in the environment, otherwise returns null.
- Value *getValue(const Expr &E, SkipPast SP) const;
- /// Transfers ownership of `Loc` to the analysis context and returns a
- /// reference to it.
- ///
- /// Requirements:
- ///
- /// `Loc` must not be null.
- template <typename T>
- std::enable_if_t<std::is_base_of<StorageLocation, T>::value, T &>
- takeOwnership(std::unique_ptr<T> Loc) {
- return DACtx->takeOwnership(std::move(Loc));
- }
- /// Transfers ownership of `Val` to the analysis context and returns a
- /// reference to it.
- ///
- /// Requirements:
- ///
- /// `Val` must not be null.
- template <typename T>
- std::enable_if_t<std::is_base_of<Value, T>::value, T &>
- takeOwnership(std::unique_ptr<T> Val) {
- return DACtx->takeOwnership(std::move(Val));
- }
- /// Returns a symbolic boolean value that models a boolean literal equal to
- /// `Value`
- AtomicBoolValue &getBoolLiteralValue(bool Value) const {
- return DACtx->getBoolLiteralValue(Value);
- }
- /// Returns an atomic boolean value.
- BoolValue &makeAtomicBoolValue() const {
- return DACtx->createAtomicBoolValue();
- }
- /// Returns a unique instance of boolean Top.
- BoolValue &makeTopBoolValue() const {
- return DACtx->createTopBoolValue();
- }
- /// Returns a boolean value that represents the conjunction of `LHS` and
- /// `RHS`. Subsequent calls with the same arguments, regardless of their
- /// order, will return the same result. If the given boolean values represent
- /// the same value, the result will be the value itself.
- BoolValue &makeAnd(BoolValue &LHS, BoolValue &RHS) const {
- return DACtx->getOrCreateConjunction(LHS, RHS);
- }
- /// Returns a boolean value that represents the disjunction of `LHS` and
- /// `RHS`. Subsequent calls with the same arguments, regardless of their
- /// order, will return the same result. If the given boolean values represent
- /// the same value, the result will be the value itself.
- BoolValue &makeOr(BoolValue &LHS, BoolValue &RHS) const {
- return DACtx->getOrCreateDisjunction(LHS, RHS);
- }
- /// Returns a boolean value that represents the negation of `Val`. Subsequent
- /// calls with the same argument will return the same result.
- BoolValue &makeNot(BoolValue &Val) const {
- return DACtx->getOrCreateNegation(Val);
- }
- /// Returns a boolean value represents `LHS` => `RHS`. Subsequent calls with
- /// the same arguments, will return the same result. If the given boolean
- /// values represent the same value, the result will be a value that
- /// represents the true boolean literal.
- BoolValue &makeImplication(BoolValue &LHS, BoolValue &RHS) const {
- return DACtx->getOrCreateImplication(LHS, RHS);
- }
- /// Returns a boolean value represents `LHS` <=> `RHS`. Subsequent calls with
- /// the same arguments, regardless of their order, will return the same
- /// result. If the given boolean values represent the same value, the result
- /// will be a value that represents the true boolean literal.
- BoolValue &makeIff(BoolValue &LHS, BoolValue &RHS) const {
- return DACtx->getOrCreateIff(LHS, RHS);
- }
- /// Returns the token that identifies the flow condition of the environment.
- AtomicBoolValue &getFlowConditionToken() const { return *FlowConditionToken; }
- /// Builds and returns the logical formula defining the flow condition
- /// identified by `Token`. If a value in the formula is present as a key in
- /// `Substitutions`, it will be substituted with the value it maps to.
- BoolValue &buildAndSubstituteFlowCondition(
- AtomicBoolValue &Token,
- llvm::DenseMap<AtomicBoolValue *, BoolValue *> Substitutions) {
- return DACtx->buildAndSubstituteFlowCondition(Token,
- std::move(Substitutions));
- }
- /// Adds `Val` to the set of clauses that constitute the flow condition.
- void addToFlowCondition(BoolValue &Val);
- /// Returns true if and only if the clauses that constitute the flow condition
- /// imply that `Val` is true.
- bool flowConditionImplies(BoolValue &Val) const;
- /// Returns the `DeclContext` of the block being analysed, if any. Otherwise,
- /// returns null.
- const DeclContext *getDeclCtx() const { return CallStack.back(); }
- /// Returns whether this `Environment` can be extended to analyze the given
- /// `Callee` (i.e. if `pushCall` can be used), with recursion disallowed and a
- /// given `MaxDepth`.
- bool canDescend(unsigned MaxDepth, const DeclContext *Callee) const;
- /// Returns the `ControlFlowContext` registered for `F`, if any. Otherwise,
- /// returns null.
- const ControlFlowContext *getControlFlowContext(const FunctionDecl *F) {
- return DACtx->getControlFlowContext(F);
- }
- LLVM_DUMP_METHOD void dump() const;
- LLVM_DUMP_METHOD void dump(raw_ostream &OS) const;
- private:
- /// Creates a value appropriate for `Type`, if `Type` is supported, otherwise
- /// return null.
- ///
- /// Recursively initializes storage locations and values until it sees a
- /// self-referential pointer or reference type. `Visited` is used to track
- /// which types appeared in the reference/pointer chain in order to avoid
- /// creating a cyclic dependency with self-referential pointers/references.
- ///
- /// Requirements:
- ///
- /// `Type` must not be null.
- Value *createValueUnlessSelfReferential(QualType Type,
- llvm::DenseSet<QualType> &Visited,
- int Depth, int &CreatedValuesCount);
- StorageLocation &skip(StorageLocation &Loc, SkipPast SP) const;
- const StorageLocation &skip(const StorageLocation &Loc, SkipPast SP) const;
- /// Shared implementation of `pushCall` overloads. Note that unlike
- /// `pushCall`, this member is invoked on the environment of the callee, not
- /// of the caller.
- void pushCallInternal(const FunctionDecl *FuncDecl,
- ArrayRef<const Expr *> Args);
- /// Assigns storage locations and values to all variables in `Vars`.
- void initVars(llvm::DenseSet<const VarDecl *> Vars);
- // `DACtx` is not null and not owned by this object.
- DataflowAnalysisContext *DACtx;
- // FIXME: move the fields `CallStack`, `ReturnLoc` and `ThisPointeeLoc` into a
- // separate call-context object, shared between environments in the same call.
- // https://github.com/llvm/llvm-project/issues/59005
- // `DeclContext` of the block being analysed if provided.
- std::vector<const DeclContext *> CallStack;
- // In a properly initialized `Environment`, `ReturnLoc` should only be null if
- // its `DeclContext` could not be cast to a `FunctionDecl`.
- StorageLocation *ReturnLoc = nullptr;
- // The storage location of the `this` pointee. Should only be null if the
- // function being analyzed is only a function and not a method.
- StorageLocation *ThisPointeeLoc = nullptr;
- // Maps from program declarations and statements to storage locations that are
- // assigned to them. Unlike the maps in `DataflowAnalysisContext`, these
- // include only storage locations that are in scope for a particular basic
- // block.
- llvm::DenseMap<const ValueDecl *, StorageLocation *> DeclToLoc;
- llvm::DenseMap<const Expr *, StorageLocation *> ExprToLoc;
- llvm::DenseMap<const StorageLocation *, Value *> LocToVal;
- // Maps locations of struct members to symbolic values of the structs that own
- // them and the decls of the struct members.
- llvm::DenseMap<const StorageLocation *,
- std::pair<StructValue *, const ValueDecl *>>
- MemberLocToStruct;
- AtomicBoolValue *FlowConditionToken;
- };
- } // namespace dataflow
- } // namespace clang
- #endif // LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_DATAFLOWENVIRONMENT_H
- #ifdef __GNUC__
- #pragma GCC diagnostic pop
- #endif
|