Path: blob/main/contrib/llvm-project/clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
35266 views
//===-- DataflowAnalysisContext.cpp -----------------------------*- C++ -*-===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This file defines a DataflowAnalysisContext class that owns objects that9// encompass the state of a program and stores context that is used during10// dataflow analysis.11//12//===----------------------------------------------------------------------===//1314#include "clang/Analysis/FlowSensitive/DataflowAnalysisContext.h"15#include "clang/AST/ExprCXX.h"16#include "clang/Analysis/FlowSensitive/ASTOps.h"17#include "clang/Analysis/FlowSensitive/DebugSupport.h"18#include "clang/Analysis/FlowSensitive/Formula.h"19#include "clang/Analysis/FlowSensitive/Logger.h"20#include "clang/Analysis/FlowSensitive/SimplifyConstraints.h"21#include "clang/Analysis/FlowSensitive/Value.h"22#include "llvm/ADT/SetOperations.h"23#include "llvm/ADT/SetVector.h"24#include "llvm/Support/CommandLine.h"25#include "llvm/Support/Debug.h"26#include "llvm/Support/FileSystem.h"27#include "llvm/Support/Path.h"28#include "llvm/Support/raw_ostream.h"29#include <cassert>30#include <memory>31#include <string>32#include <utility>33#include <vector>3435static llvm::cl::opt<std::string> DataflowLog(36"dataflow-log", llvm::cl::Hidden, llvm::cl::ValueOptional,37llvm::cl::desc("Emit log of dataflow analysis. With no arg, writes textual "38"log to stderr. With an arg, writes HTML logs under the "39"specified directory (one per analyzed function)."));4041namespace clang {42namespace dataflow {4344FieldSet DataflowAnalysisContext::getModeledFields(QualType Type) {45// During context-sensitive analysis, a struct may be allocated in one46// function, but its field accessed in a function lower in the stack than47// the allocation. Since we only collect fields used in the function where48// the allocation occurs, we can't apply that filter when performing49// context-sensitive analysis. But, this only applies to storage locations,50// since field access it not allowed to fail. In contrast, field *values*51// don't need this allowance, since the API allows for uninitialized fields.52if (Opts.ContextSensitiveOpts)53return getObjectFields(Type);5455return llvm::set_intersection(getObjectFields(Type), ModeledFields);56}5758void DataflowAnalysisContext::addModeledFields(const FieldSet &Fields) {59ModeledFields.set_union(Fields);60}6162StorageLocation &DataflowAnalysisContext::createStorageLocation(QualType Type) {63if (!Type.isNull() && Type->isRecordType()) {64llvm::DenseMap<const ValueDecl *, StorageLocation *> FieldLocs;65for (const FieldDecl *Field : getModeledFields(Type))66if (Field->getType()->isReferenceType())67FieldLocs.insert({Field, nullptr});68else69FieldLocs.insert({Field, &createStorageLocation(70Field->getType().getNonReferenceType())});7172RecordStorageLocation::SyntheticFieldMap SyntheticFields;73for (const auto &Entry : getSyntheticFields(Type))74SyntheticFields.insert(75{Entry.getKey(),76&createStorageLocation(Entry.getValue().getNonReferenceType())});7778return createRecordStorageLocation(Type, std::move(FieldLocs),79std::move(SyntheticFields));80}81return arena().create<ScalarStorageLocation>(Type);82}8384// Returns the keys for a given `StringMap`.85// Can't use `StringSet` as the return type as it doesn't support `operator==`.86template <typename T>87static llvm::DenseSet<llvm::StringRef> getKeys(const llvm::StringMap<T> &Map) {88return llvm::DenseSet<llvm::StringRef>(Map.keys().begin(), Map.keys().end());89}9091RecordStorageLocation &DataflowAnalysisContext::createRecordStorageLocation(92QualType Type, RecordStorageLocation::FieldToLoc FieldLocs,93RecordStorageLocation::SyntheticFieldMap SyntheticFields) {94assert(Type->isRecordType());95assert(containsSameFields(getModeledFields(Type), FieldLocs));96assert(getKeys(getSyntheticFields(Type)) == getKeys(SyntheticFields));9798RecordStorageLocationCreated = true;99return arena().create<RecordStorageLocation>(Type, std::move(FieldLocs),100std::move(SyntheticFields));101}102103StorageLocation &104DataflowAnalysisContext::getStableStorageLocation(const ValueDecl &D) {105if (auto *Loc = DeclToLoc.lookup(&D))106return *Loc;107auto &Loc = createStorageLocation(D.getType().getNonReferenceType());108DeclToLoc[&D] = &Loc;109return Loc;110}111112StorageLocation &113DataflowAnalysisContext::getStableStorageLocation(const Expr &E) {114const Expr &CanonE = ignoreCFGOmittedNodes(E);115116if (auto *Loc = ExprToLoc.lookup(&CanonE))117return *Loc;118auto &Loc = createStorageLocation(CanonE.getType());119ExprToLoc[&CanonE] = &Loc;120return Loc;121}122123PointerValue &124DataflowAnalysisContext::getOrCreateNullPointerValue(QualType PointeeType) {125auto CanonicalPointeeType =126PointeeType.isNull() ? PointeeType : PointeeType.getCanonicalType();127auto Res = NullPointerVals.try_emplace(CanonicalPointeeType, nullptr);128if (Res.second) {129auto &PointeeLoc = createStorageLocation(CanonicalPointeeType);130Res.first->second = &arena().create<PointerValue>(PointeeLoc);131}132return *Res.first->second;133}134135void DataflowAnalysisContext::addInvariant(const Formula &Constraint) {136if (Invariant == nullptr)137Invariant = &Constraint;138else139Invariant = &arena().makeAnd(*Invariant, Constraint);140}141142void DataflowAnalysisContext::addFlowConditionConstraint(143Atom Token, const Formula &Constraint) {144auto Res = FlowConditionConstraints.try_emplace(Token, &Constraint);145if (!Res.second) {146Res.first->second =147&arena().makeAnd(*Res.first->second, Constraint);148}149}150151Atom DataflowAnalysisContext::forkFlowCondition(Atom Token) {152Atom ForkToken = arena().makeFlowConditionToken();153FlowConditionDeps[ForkToken].insert(Token);154addFlowConditionConstraint(ForkToken, arena().makeAtomRef(Token));155return ForkToken;156}157158Atom159DataflowAnalysisContext::joinFlowConditions(Atom FirstToken,160Atom SecondToken) {161Atom Token = arena().makeFlowConditionToken();162FlowConditionDeps[Token].insert(FirstToken);163FlowConditionDeps[Token].insert(SecondToken);164addFlowConditionConstraint(Token,165arena().makeOr(arena().makeAtomRef(FirstToken),166arena().makeAtomRef(SecondToken)));167return Token;168}169170Solver::Result DataflowAnalysisContext::querySolver(171llvm::SetVector<const Formula *> Constraints) {172return S.solve(Constraints.getArrayRef());173}174175bool DataflowAnalysisContext::flowConditionImplies(Atom Token,176const Formula &F) {177if (F.isLiteral(true))178return true;179180// Returns true if and only if truth assignment of the flow condition implies181// that `F` is also true. We prove whether or not this property holds by182// reducing the problem to satisfiability checking. In other words, we attempt183// to show that assuming `F` is false makes the constraints induced by the184// flow condition unsatisfiable.185llvm::SetVector<const Formula *> Constraints;186Constraints.insert(&arena().makeAtomRef(Token));187Constraints.insert(&arena().makeNot(F));188addTransitiveFlowConditionConstraints(Token, Constraints);189return isUnsatisfiable(std::move(Constraints));190}191192bool DataflowAnalysisContext::flowConditionAllows(Atom Token,193const Formula &F) {194if (F.isLiteral(false))195return false;196197llvm::SetVector<const Formula *> Constraints;198Constraints.insert(&arena().makeAtomRef(Token));199Constraints.insert(&F);200addTransitiveFlowConditionConstraints(Token, Constraints);201return isSatisfiable(std::move(Constraints));202}203204bool DataflowAnalysisContext::equivalentFormulas(const Formula &Val1,205const Formula &Val2) {206llvm::SetVector<const Formula *> Constraints;207Constraints.insert(&arena().makeNot(arena().makeEquals(Val1, Val2)));208return isUnsatisfiable(std::move(Constraints));209}210211void DataflowAnalysisContext::addTransitiveFlowConditionConstraints(212Atom Token, llvm::SetVector<const Formula *> &Constraints) {213llvm::DenseSet<Atom> AddedTokens;214std::vector<Atom> Remaining = {Token};215216if (Invariant)217Constraints.insert(Invariant);218// Define all the flow conditions that might be referenced in constraints.219while (!Remaining.empty()) {220auto Token = Remaining.back();221Remaining.pop_back();222if (!AddedTokens.insert(Token).second)223continue;224225auto ConstraintsIt = FlowConditionConstraints.find(Token);226if (ConstraintsIt == FlowConditionConstraints.end()) {227Constraints.insert(&arena().makeAtomRef(Token));228} else {229// Bind flow condition token via `iff` to its set of constraints:230// FC <=> (C1 ^ C2 ^ ...), where Ci are constraints231Constraints.insert(&arena().makeEquals(arena().makeAtomRef(Token),232*ConstraintsIt->second));233}234235if (auto DepsIt = FlowConditionDeps.find(Token);236DepsIt != FlowConditionDeps.end())237for (Atom A : DepsIt->second)238Remaining.push_back(A);239}240}241242static void printAtomList(const llvm::SmallVector<Atom> &Atoms,243llvm::raw_ostream &OS) {244OS << "(";245for (size_t i = 0; i < Atoms.size(); ++i) {246OS << Atoms[i];247if (i + 1 < Atoms.size())248OS << ", ";249}250OS << ")\n";251}252253void DataflowAnalysisContext::dumpFlowCondition(Atom Token,254llvm::raw_ostream &OS) {255llvm::SetVector<const Formula *> Constraints;256Constraints.insert(&arena().makeAtomRef(Token));257addTransitiveFlowConditionConstraints(Token, Constraints);258259OS << "Flow condition token: " << Token << "\n";260SimplifyConstraintsInfo Info;261llvm::SetVector<const Formula *> OriginalConstraints = Constraints;262simplifyConstraints(Constraints, arena(), &Info);263if (!Constraints.empty()) {264OS << "Constraints:\n";265for (const auto *Constraint : Constraints) {266Constraint->print(OS);267OS << "\n";268}269}270if (!Info.TrueAtoms.empty()) {271OS << "True atoms: ";272printAtomList(Info.TrueAtoms, OS);273}274if (!Info.FalseAtoms.empty()) {275OS << "False atoms: ";276printAtomList(Info.FalseAtoms, OS);277}278if (!Info.EquivalentAtoms.empty()) {279OS << "Equivalent atoms:\n";280for (const llvm::SmallVector<Atom> &Class : Info.EquivalentAtoms)281printAtomList(Class, OS);282}283284OS << "\nFlow condition constraints before simplification:\n";285for (const auto *Constraint : OriginalConstraints) {286Constraint->print(OS);287OS << "\n";288}289}290291const AdornedCFG *292DataflowAnalysisContext::getAdornedCFG(const FunctionDecl *F) {293// Canonicalize the key:294F = F->getDefinition();295if (F == nullptr)296return nullptr;297auto It = FunctionContexts.find(F);298if (It != FunctionContexts.end())299return &It->second;300301if (F->doesThisDeclarationHaveABody()) {302auto ACFG = AdornedCFG::build(*F);303// FIXME: Handle errors.304assert(ACFG);305auto Result = FunctionContexts.insert({F, std::move(*ACFG)});306return &Result.first->second;307}308309return nullptr;310}311312static std::unique_ptr<Logger> makeLoggerFromCommandLine() {313if (DataflowLog.empty())314return Logger::textual(llvm::errs());315316llvm::StringRef Dir = DataflowLog;317if (auto EC = llvm::sys::fs::create_directories(Dir))318llvm::errs() << "Failed to create log dir: " << EC.message() << "\n";319// All analysis runs within a process will log to the same directory.320// Share a counter so they don't all overwrite each other's 0.html.321// (Don't share a logger, it's not threadsafe).322static std::atomic<unsigned> Counter = {0};323auto StreamFactory =324[Dir(Dir.str())]() mutable -> std::unique_ptr<llvm::raw_ostream> {325llvm::SmallString<256> File(Dir);326llvm::sys::path::append(File,327std::to_string(Counter.fetch_add(1)) + ".html");328std::error_code EC;329auto OS = std::make_unique<llvm::raw_fd_ostream>(File, EC);330if (EC) {331llvm::errs() << "Failed to create log " << File << ": " << EC.message()332<< "\n";333return std::make_unique<llvm::raw_null_ostream>();334}335return OS;336};337return Logger::html(std::move(StreamFactory));338}339340DataflowAnalysisContext::DataflowAnalysisContext(341Solver &S, std::unique_ptr<Solver> &&OwnedSolver, Options Opts)342: S(S), OwnedSolver(std::move(OwnedSolver)), A(std::make_unique<Arena>()),343Opts(Opts) {344// If the -dataflow-log command-line flag was set, synthesize a logger.345// This is ugly but provides a uniform method for ad-hoc debugging dataflow-346// based tools.347if (Opts.Log == nullptr) {348if (DataflowLog.getNumOccurrences() > 0) {349LogOwner = makeLoggerFromCommandLine();350this->Opts.Log = LogOwner.get();351// FIXME: if the flag is given a value, write an HTML log to a file.352} else {353this->Opts.Log = &Logger::null();354}355}356}357358DataflowAnalysisContext::~DataflowAnalysisContext() = default;359360} // namespace dataflow361} // namespace clang362363364