xref: /freebsd/contrib/llvm-project/clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp (revision 7a6dacaca14b62ca4b74406814becb87a3fefac0)
181ad6265SDimitry Andric //===-- DataflowAnalysisContext.cpp -----------------------------*- C++ -*-===//
281ad6265SDimitry Andric //
381ad6265SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
481ad6265SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
581ad6265SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
681ad6265SDimitry Andric //
781ad6265SDimitry Andric //===----------------------------------------------------------------------===//
881ad6265SDimitry Andric //
981ad6265SDimitry Andric //  This file defines a DataflowAnalysisContext class that owns objects that
1081ad6265SDimitry Andric //  encompass the state of a program and stores context that is used during
1181ad6265SDimitry Andric //  dataflow analysis.
1281ad6265SDimitry Andric //
1381ad6265SDimitry Andric //===----------------------------------------------------------------------===//
1481ad6265SDimitry Andric 
1581ad6265SDimitry Andric #include "clang/Analysis/FlowSensitive/DataflowAnalysisContext.h"
1681ad6265SDimitry Andric #include "clang/AST/ExprCXX.h"
17fcaf7f86SDimitry Andric #include "clang/Analysis/FlowSensitive/DebugSupport.h"
1806c3fb27SDimitry Andric #include "clang/Analysis/FlowSensitive/Formula.h"
1906c3fb27SDimitry Andric #include "clang/Analysis/FlowSensitive/Logger.h"
205f757f3fSDimitry Andric #include "clang/Analysis/FlowSensitive/SimplifyConstraints.h"
2181ad6265SDimitry Andric #include "clang/Analysis/FlowSensitive/Value.h"
22bdd1243dSDimitry Andric #include "llvm/ADT/SetOperations.h"
2306c3fb27SDimitry Andric #include "llvm/ADT/SetVector.h"
2406c3fb27SDimitry Andric #include "llvm/Support/CommandLine.h"
25fcaf7f86SDimitry Andric #include "llvm/Support/Debug.h"
2606c3fb27SDimitry Andric #include "llvm/Support/FileSystem.h"
2706c3fb27SDimitry Andric #include "llvm/Support/Path.h"
2806c3fb27SDimitry Andric #include "llvm/Support/raw_ostream.h"
2981ad6265SDimitry Andric #include <cassert>
3081ad6265SDimitry Andric #include <memory>
3106c3fb27SDimitry Andric #include <string>
3281ad6265SDimitry Andric #include <utility>
3306c3fb27SDimitry Andric #include <vector>
3406c3fb27SDimitry Andric 
3506c3fb27SDimitry Andric static llvm::cl::opt<std::string> DataflowLog(
3606c3fb27SDimitry Andric     "dataflow-log", llvm::cl::Hidden, llvm::cl::ValueOptional,
3706c3fb27SDimitry Andric     llvm::cl::desc("Emit log of dataflow analysis. With no arg, writes textual "
3806c3fb27SDimitry Andric                    "log to stderr. With an arg, writes HTML logs under the "
3906c3fb27SDimitry Andric                    "specified directory (one per analyzed function)."));
4081ad6265SDimitry Andric 
4181ad6265SDimitry Andric namespace clang {
4281ad6265SDimitry Andric namespace dataflow {
4381ad6265SDimitry Andric 
4406c3fb27SDimitry Andric FieldSet DataflowAnalysisContext::getModeledFields(QualType Type) {
45bdd1243dSDimitry Andric   // During context-sensitive analysis, a struct may be allocated in one
46bdd1243dSDimitry Andric   // function, but its field accessed in a function lower in the stack than
47bdd1243dSDimitry Andric   // the allocation. Since we only collect fields used in the function where
48bdd1243dSDimitry Andric   // the allocation occurs, we can't apply that filter when performing
49bdd1243dSDimitry Andric   // context-sensitive analysis. But, this only applies to storage locations,
50bdd1243dSDimitry Andric   // since field access it not allowed to fail. In contrast, field *values*
51bdd1243dSDimitry Andric   // don't need this allowance, since the API allows for uninitialized fields.
5206c3fb27SDimitry Andric   if (Opts.ContextSensitiveOpts)
5306c3fb27SDimitry Andric     return getObjectFields(Type);
5406c3fb27SDimitry Andric 
5506c3fb27SDimitry Andric   return llvm::set_intersection(getObjectFields(Type), ModeledFields);
5681ad6265SDimitry Andric }
5706c3fb27SDimitry Andric 
5806c3fb27SDimitry Andric void DataflowAnalysisContext::addModeledFields(const FieldSet &Fields) {
5906c3fb27SDimitry Andric   ModeledFields.set_union(Fields);
6006c3fb27SDimitry Andric }
6106c3fb27SDimitry Andric 
6206c3fb27SDimitry Andric StorageLocation &DataflowAnalysisContext::createStorageLocation(QualType Type) {
6306c3fb27SDimitry Andric   if (!Type.isNull() && Type->isRecordType()) {
6406c3fb27SDimitry Andric     llvm::DenseMap<const ValueDecl *, StorageLocation *> FieldLocs;
6506c3fb27SDimitry Andric     for (const FieldDecl *Field : getModeledFields(Type))
6606c3fb27SDimitry Andric       if (Field->getType()->isReferenceType())
6706c3fb27SDimitry Andric         FieldLocs.insert({Field, nullptr});
6806c3fb27SDimitry Andric       else
6906c3fb27SDimitry Andric         FieldLocs.insert({Field, &createStorageLocation(
7006c3fb27SDimitry Andric                                      Field->getType().getNonReferenceType())});
715f757f3fSDimitry Andric 
725f757f3fSDimitry Andric     RecordStorageLocation::SyntheticFieldMap SyntheticFields;
735f757f3fSDimitry Andric     for (const auto &Entry : getSyntheticFields(Type))
745f757f3fSDimitry Andric       SyntheticFields.insert(
755f757f3fSDimitry Andric           {Entry.getKey(),
765f757f3fSDimitry Andric            &createStorageLocation(Entry.getValue().getNonReferenceType())});
775f757f3fSDimitry Andric 
785f757f3fSDimitry Andric     return createRecordStorageLocation(Type, std::move(FieldLocs),
795f757f3fSDimitry Andric                                        std::move(SyntheticFields));
8006c3fb27SDimitry Andric   }
8106c3fb27SDimitry Andric   return arena().create<ScalarStorageLocation>(Type);
8281ad6265SDimitry Andric }
8381ad6265SDimitry Andric 
845f757f3fSDimitry Andric // Returns the keys for a given `StringMap`.
855f757f3fSDimitry Andric // Can't use `StringSet` as the return type as it doesn't support `operator==`.
865f757f3fSDimitry Andric template <typename T>
875f757f3fSDimitry Andric static llvm::DenseSet<llvm::StringRef> getKeys(const llvm::StringMap<T> &Map) {
885f757f3fSDimitry Andric   return llvm::DenseSet<llvm::StringRef>(Map.keys().begin(), Map.keys().end());
895f757f3fSDimitry Andric }
905f757f3fSDimitry Andric 
915f757f3fSDimitry Andric RecordStorageLocation &DataflowAnalysisContext::createRecordStorageLocation(
925f757f3fSDimitry Andric     QualType Type, RecordStorageLocation::FieldToLoc FieldLocs,
935f757f3fSDimitry Andric     RecordStorageLocation::SyntheticFieldMap SyntheticFields) {
945f757f3fSDimitry Andric   assert(Type->isRecordType());
955f757f3fSDimitry Andric   assert(containsSameFields(getModeledFields(Type), FieldLocs));
965f757f3fSDimitry Andric   assert(getKeys(getSyntheticFields(Type)) == getKeys(SyntheticFields));
975f757f3fSDimitry Andric 
985f757f3fSDimitry Andric   RecordStorageLocationCreated = true;
995f757f3fSDimitry Andric   return arena().create<RecordStorageLocation>(Type, std::move(FieldLocs),
1005f757f3fSDimitry Andric                                                std::move(SyntheticFields));
1015f757f3fSDimitry Andric }
1025f757f3fSDimitry Andric 
10381ad6265SDimitry Andric StorageLocation &
1045f757f3fSDimitry Andric DataflowAnalysisContext::getStableStorageLocation(const ValueDecl &D) {
1055f757f3fSDimitry Andric   if (auto *Loc = DeclToLoc.lookup(&D))
10681ad6265SDimitry Andric     return *Loc;
10706c3fb27SDimitry Andric   auto &Loc = createStorageLocation(D.getType().getNonReferenceType());
1085f757f3fSDimitry Andric   DeclToLoc[&D] = &Loc;
10981ad6265SDimitry Andric   return Loc;
11081ad6265SDimitry Andric }
11181ad6265SDimitry Andric 
11281ad6265SDimitry Andric StorageLocation &
11381ad6265SDimitry Andric DataflowAnalysisContext::getStableStorageLocation(const Expr &E) {
1145f757f3fSDimitry Andric   const Expr &CanonE = ignoreCFGOmittedNodes(E);
1155f757f3fSDimitry Andric 
1165f757f3fSDimitry Andric   if (auto *Loc = ExprToLoc.lookup(&CanonE))
11781ad6265SDimitry Andric     return *Loc;
1185f757f3fSDimitry Andric   auto &Loc = createStorageLocation(CanonE.getType());
1195f757f3fSDimitry Andric   ExprToLoc[&CanonE] = &Loc;
12081ad6265SDimitry Andric   return Loc;
12181ad6265SDimitry Andric }
12281ad6265SDimitry Andric 
12381ad6265SDimitry Andric PointerValue &
12481ad6265SDimitry Andric DataflowAnalysisContext::getOrCreateNullPointerValue(QualType PointeeType) {
125753f127fSDimitry Andric   auto CanonicalPointeeType =
126753f127fSDimitry Andric       PointeeType.isNull() ? PointeeType : PointeeType.getCanonicalType();
12781ad6265SDimitry Andric   auto Res = NullPointerVals.try_emplace(CanonicalPointeeType, nullptr);
12881ad6265SDimitry Andric   if (Res.second) {
129bdd1243dSDimitry Andric     auto &PointeeLoc = createStorageLocation(CanonicalPointeeType);
13006c3fb27SDimitry Andric     Res.first->second = &arena().create<PointerValue>(PointeeLoc);
13181ad6265SDimitry Andric   }
13281ad6265SDimitry Andric   return *Res.first->second;
13381ad6265SDimitry Andric }
13481ad6265SDimitry Andric 
1355f757f3fSDimitry Andric void DataflowAnalysisContext::addInvariant(const Formula &Constraint) {
1365f757f3fSDimitry Andric   if (Invariant == nullptr)
1375f757f3fSDimitry Andric     Invariant = &Constraint;
1385f757f3fSDimitry Andric   else
1395f757f3fSDimitry Andric     Invariant = &arena().makeAnd(*Invariant, Constraint);
1405f757f3fSDimitry Andric }
1415f757f3fSDimitry Andric 
14281ad6265SDimitry Andric void DataflowAnalysisContext::addFlowConditionConstraint(
14306c3fb27SDimitry Andric     Atom Token, const Formula &Constraint) {
14406c3fb27SDimitry Andric   auto Res = FlowConditionConstraints.try_emplace(Token, &Constraint);
14581ad6265SDimitry Andric   if (!Res.second) {
14606c3fb27SDimitry Andric     Res.first->second =
14706c3fb27SDimitry Andric         &arena().makeAnd(*Res.first->second, Constraint);
14881ad6265SDimitry Andric   }
14981ad6265SDimitry Andric }
15081ad6265SDimitry Andric 
15106c3fb27SDimitry Andric Atom DataflowAnalysisContext::forkFlowCondition(Atom Token) {
15206c3fb27SDimitry Andric   Atom ForkToken = arena().makeFlowConditionToken();
15306c3fb27SDimitry Andric   FlowConditionDeps[ForkToken].insert(Token);
15406c3fb27SDimitry Andric   addFlowConditionConstraint(ForkToken, arena().makeAtomRef(Token));
15581ad6265SDimitry Andric   return ForkToken;
15681ad6265SDimitry Andric }
15781ad6265SDimitry Andric 
15806c3fb27SDimitry Andric Atom
15906c3fb27SDimitry Andric DataflowAnalysisContext::joinFlowConditions(Atom FirstToken,
16006c3fb27SDimitry Andric                                             Atom SecondToken) {
16106c3fb27SDimitry Andric   Atom Token = arena().makeFlowConditionToken();
16206c3fb27SDimitry Andric   FlowConditionDeps[Token].insert(FirstToken);
16306c3fb27SDimitry Andric   FlowConditionDeps[Token].insert(SecondToken);
16481ad6265SDimitry Andric   addFlowConditionConstraint(Token,
16506c3fb27SDimitry Andric                              arena().makeOr(arena().makeAtomRef(FirstToken),
16606c3fb27SDimitry Andric                                             arena().makeAtomRef(SecondToken)));
16781ad6265SDimitry Andric   return Token;
16881ad6265SDimitry Andric }
16981ad6265SDimitry Andric 
17006c3fb27SDimitry Andric Solver::Result DataflowAnalysisContext::querySolver(
17106c3fb27SDimitry Andric     llvm::SetVector<const Formula *> Constraints) {
17206c3fb27SDimitry Andric   return S->solve(Constraints.getArrayRef());
17381ad6265SDimitry Andric }
17481ad6265SDimitry Andric 
17506c3fb27SDimitry Andric bool DataflowAnalysisContext::flowConditionImplies(Atom Token,
1765f757f3fSDimitry Andric                                                    const Formula &F) {
177*7a6dacacSDimitry Andric   if (F.isLiteral(true))
178*7a6dacacSDimitry Andric     return true;
179*7a6dacacSDimitry Andric 
18081ad6265SDimitry Andric   // Returns true if and only if truth assignment of the flow condition implies
1815f757f3fSDimitry Andric   // that `F` is also true. We prove whether or not this property holds by
18281ad6265SDimitry Andric   // reducing the problem to satisfiability checking. In other words, we attempt
1835f757f3fSDimitry Andric   // to show that assuming `F` is false makes the constraints induced by the
18481ad6265SDimitry Andric   // flow condition unsatisfiable.
18506c3fb27SDimitry Andric   llvm::SetVector<const Formula *> Constraints;
18606c3fb27SDimitry Andric   Constraints.insert(&arena().makeAtomRef(Token));
1875f757f3fSDimitry Andric   Constraints.insert(&arena().makeNot(F));
1885f757f3fSDimitry Andric   addTransitiveFlowConditionConstraints(Token, Constraints);
18981ad6265SDimitry Andric   return isUnsatisfiable(std::move(Constraints));
19081ad6265SDimitry Andric }
19181ad6265SDimitry Andric 
1925f757f3fSDimitry Andric bool DataflowAnalysisContext::flowConditionAllows(Atom Token,
1935f757f3fSDimitry Andric                                                   const Formula &F) {
194*7a6dacacSDimitry Andric   if (F.isLiteral(false))
195*7a6dacacSDimitry Andric     return false;
196*7a6dacacSDimitry Andric 
19706c3fb27SDimitry Andric   llvm::SetVector<const Formula *> Constraints;
1985f757f3fSDimitry Andric   Constraints.insert(&arena().makeAtomRef(Token));
1995f757f3fSDimitry Andric   Constraints.insert(&F);
2005f757f3fSDimitry Andric   addTransitiveFlowConditionConstraints(Token, Constraints);
2015f757f3fSDimitry Andric   return isSatisfiable(std::move(Constraints));
20281ad6265SDimitry Andric }
20381ad6265SDimitry Andric 
20406c3fb27SDimitry Andric bool DataflowAnalysisContext::equivalentFormulas(const Formula &Val1,
20506c3fb27SDimitry Andric                                                  const Formula &Val2) {
20606c3fb27SDimitry Andric   llvm::SetVector<const Formula *> Constraints;
20706c3fb27SDimitry Andric   Constraints.insert(&arena().makeNot(arena().makeEquals(Val1, Val2)));
20806c3fb27SDimitry Andric   return isUnsatisfiable(std::move(Constraints));
20981ad6265SDimitry Andric }
21081ad6265SDimitry Andric 
21181ad6265SDimitry Andric void DataflowAnalysisContext::addTransitiveFlowConditionConstraints(
2125f757f3fSDimitry Andric     Atom Token, llvm::SetVector<const Formula *> &Constraints) {
2135f757f3fSDimitry Andric   llvm::DenseSet<Atom> AddedTokens;
2145f757f3fSDimitry Andric   std::vector<Atom> Remaining = {Token};
2155f757f3fSDimitry Andric 
2165f757f3fSDimitry Andric   if (Invariant)
2175f757f3fSDimitry Andric     Constraints.insert(Invariant);
2185f757f3fSDimitry Andric   // Define all the flow conditions that might be referenced in constraints.
2195f757f3fSDimitry Andric   while (!Remaining.empty()) {
2205f757f3fSDimitry Andric     auto Token = Remaining.back();
2215f757f3fSDimitry Andric     Remaining.pop_back();
2225f757f3fSDimitry Andric     if (!AddedTokens.insert(Token).second)
2235f757f3fSDimitry Andric       continue;
22481ad6265SDimitry Andric 
22506c3fb27SDimitry Andric     auto ConstraintsIt = FlowConditionConstraints.find(Token);
226972a253aSDimitry Andric     if (ConstraintsIt == FlowConditionConstraints.end()) {
22706c3fb27SDimitry Andric       Constraints.insert(&arena().makeAtomRef(Token));
22881ad6265SDimitry Andric     } else {
22981ad6265SDimitry Andric       // Bind flow condition token via `iff` to its set of constraints:
23081ad6265SDimitry Andric       // FC <=> (C1 ^ C2 ^ ...), where Ci are constraints
23106c3fb27SDimitry Andric       Constraints.insert(&arena().makeEquals(arena().makeAtomRef(Token),
23206c3fb27SDimitry Andric                                              *ConstraintsIt->second));
23381ad6265SDimitry Andric     }
23481ad6265SDimitry Andric 
2355f757f3fSDimitry Andric     if (auto DepsIt = FlowConditionDeps.find(Token);
2365f757f3fSDimitry Andric         DepsIt != FlowConditionDeps.end())
2375f757f3fSDimitry Andric       for (Atom A : DepsIt->second)
2385f757f3fSDimitry Andric         Remaining.push_back(A);
23981ad6265SDimitry Andric   }
24081ad6265SDimitry Andric }
2415f757f3fSDimitry Andric 
2425f757f3fSDimitry Andric static void printAtomList(const llvm::SmallVector<Atom> &Atoms,
2435f757f3fSDimitry Andric                           llvm::raw_ostream &OS) {
2445f757f3fSDimitry Andric   OS << "(";
2455f757f3fSDimitry Andric   for (size_t i = 0; i < Atoms.size(); ++i) {
2465f757f3fSDimitry Andric     OS << Atoms[i];
2475f757f3fSDimitry Andric     if (i + 1 < Atoms.size())
2485f757f3fSDimitry Andric       OS << ", ";
2495f757f3fSDimitry Andric   }
2505f757f3fSDimitry Andric   OS << ")\n";
25181ad6265SDimitry Andric }
25281ad6265SDimitry Andric 
25306c3fb27SDimitry Andric void DataflowAnalysisContext::dumpFlowCondition(Atom Token,
25406c3fb27SDimitry Andric                                                 llvm::raw_ostream &OS) {
25506c3fb27SDimitry Andric   llvm::SetVector<const Formula *> Constraints;
25606c3fb27SDimitry Andric   Constraints.insert(&arena().makeAtomRef(Token));
2575f757f3fSDimitry Andric   addTransitiveFlowConditionConstraints(Token, Constraints);
258fcaf7f86SDimitry Andric 
2595f757f3fSDimitry Andric   OS << "Flow condition token: " << Token << "\n";
2605f757f3fSDimitry Andric   SimplifyConstraintsInfo Info;
2615f757f3fSDimitry Andric   llvm::SetVector<const Formula *> OriginalConstraints = Constraints;
2625f757f3fSDimitry Andric   simplifyConstraints(Constraints, arena(), &Info);
2635f757f3fSDimitry Andric   if (!Constraints.empty()) {
2645f757f3fSDimitry Andric     OS << "Constraints:\n";
26506c3fb27SDimitry Andric     for (const auto *Constraint : Constraints) {
2665f757f3fSDimitry Andric       Constraint->print(OS);
2675f757f3fSDimitry Andric       OS << "\n";
2685f757f3fSDimitry Andric     }
2695f757f3fSDimitry Andric   }
2705f757f3fSDimitry Andric   if (!Info.TrueAtoms.empty()) {
2715f757f3fSDimitry Andric     OS << "True atoms: ";
2725f757f3fSDimitry Andric     printAtomList(Info.TrueAtoms, OS);
2735f757f3fSDimitry Andric   }
2745f757f3fSDimitry Andric   if (!Info.FalseAtoms.empty()) {
2755f757f3fSDimitry Andric     OS << "False atoms: ";
2765f757f3fSDimitry Andric     printAtomList(Info.FalseAtoms, OS);
2775f757f3fSDimitry Andric   }
2785f757f3fSDimitry Andric   if (!Info.EquivalentAtoms.empty()) {
2795f757f3fSDimitry Andric     OS << "Equivalent atoms:\n";
2805f757f3fSDimitry Andric     for (const llvm::SmallVector<Atom> &Class : Info.EquivalentAtoms)
2815f757f3fSDimitry Andric       printAtomList(Class, OS);
2825f757f3fSDimitry Andric   }
2835f757f3fSDimitry Andric 
2845f757f3fSDimitry Andric   OS << "\nFlow condition constraints before simplification:\n";
2855f757f3fSDimitry Andric   for (const auto *Constraint : OriginalConstraints) {
2865f757f3fSDimitry Andric     Constraint->print(OS);
28706c3fb27SDimitry Andric     OS << "\n";
28806c3fb27SDimitry Andric   }
289fcaf7f86SDimitry Andric }
290fcaf7f86SDimitry Andric 
291bdd1243dSDimitry Andric const ControlFlowContext *
292bdd1243dSDimitry Andric DataflowAnalysisContext::getControlFlowContext(const FunctionDecl *F) {
293bdd1243dSDimitry Andric   // Canonicalize the key:
294bdd1243dSDimitry Andric   F = F->getDefinition();
295bdd1243dSDimitry Andric   if (F == nullptr)
296bdd1243dSDimitry Andric     return nullptr;
297bdd1243dSDimitry Andric   auto It = FunctionContexts.find(F);
298bdd1243dSDimitry Andric   if (It != FunctionContexts.end())
299bdd1243dSDimitry Andric     return &It->second;
300bdd1243dSDimitry Andric 
301*7a6dacacSDimitry Andric   if (F->doesThisDeclarationHaveABody()) {
30206c3fb27SDimitry Andric     auto CFCtx = ControlFlowContext::build(*F);
303bdd1243dSDimitry Andric     // FIXME: Handle errors.
304bdd1243dSDimitry Andric     assert(CFCtx);
305bdd1243dSDimitry Andric     auto Result = FunctionContexts.insert({F, std::move(*CFCtx)});
306bdd1243dSDimitry Andric     return &Result.first->second;
307bdd1243dSDimitry Andric   }
308bdd1243dSDimitry Andric 
309bdd1243dSDimitry Andric   return nullptr;
310bdd1243dSDimitry Andric }
311bdd1243dSDimitry Andric 
31206c3fb27SDimitry Andric static std::unique_ptr<Logger> makeLoggerFromCommandLine() {
31306c3fb27SDimitry Andric   if (DataflowLog.empty())
31406c3fb27SDimitry Andric     return Logger::textual(llvm::errs());
31506c3fb27SDimitry Andric 
31606c3fb27SDimitry Andric   llvm::StringRef Dir = DataflowLog;
31706c3fb27SDimitry Andric   if (auto EC = llvm::sys::fs::create_directories(Dir))
31806c3fb27SDimitry Andric     llvm::errs() << "Failed to create log dir: " << EC.message() << "\n";
31906c3fb27SDimitry Andric   // All analysis runs within a process will log to the same directory.
32006c3fb27SDimitry Andric   // Share a counter so they don't all overwrite each other's 0.html.
32106c3fb27SDimitry Andric   // (Don't share a logger, it's not threadsafe).
32206c3fb27SDimitry Andric   static std::atomic<unsigned> Counter = {0};
32306c3fb27SDimitry Andric   auto StreamFactory =
32406c3fb27SDimitry Andric       [Dir(Dir.str())]() mutable -> std::unique_ptr<llvm::raw_ostream> {
32506c3fb27SDimitry Andric     llvm::SmallString<256> File(Dir);
32606c3fb27SDimitry Andric     llvm::sys::path::append(File,
32706c3fb27SDimitry Andric                             std::to_string(Counter.fetch_add(1)) + ".html");
32806c3fb27SDimitry Andric     std::error_code EC;
32906c3fb27SDimitry Andric     auto OS = std::make_unique<llvm::raw_fd_ostream>(File, EC);
33006c3fb27SDimitry Andric     if (EC) {
33106c3fb27SDimitry Andric       llvm::errs() << "Failed to create log " << File << ": " << EC.message()
33206c3fb27SDimitry Andric                    << "\n";
33306c3fb27SDimitry Andric       return std::make_unique<llvm::raw_null_ostream>();
33406c3fb27SDimitry Andric     }
33506c3fb27SDimitry Andric     return OS;
33606c3fb27SDimitry Andric   };
33706c3fb27SDimitry Andric   return Logger::html(std::move(StreamFactory));
33806c3fb27SDimitry Andric }
33906c3fb27SDimitry Andric 
34006c3fb27SDimitry Andric DataflowAnalysisContext::DataflowAnalysisContext(std::unique_ptr<Solver> S,
34106c3fb27SDimitry Andric                                                  Options Opts)
34206c3fb27SDimitry Andric     : S(std::move(S)), A(std::make_unique<Arena>()), Opts(Opts) {
34306c3fb27SDimitry Andric   assert(this->S != nullptr);
34406c3fb27SDimitry Andric   // If the -dataflow-log command-line flag was set, synthesize a logger.
34506c3fb27SDimitry Andric   // This is ugly but provides a uniform method for ad-hoc debugging dataflow-
34606c3fb27SDimitry Andric   // based tools.
34706c3fb27SDimitry Andric   if (Opts.Log == nullptr) {
34806c3fb27SDimitry Andric     if (DataflowLog.getNumOccurrences() > 0) {
34906c3fb27SDimitry Andric       LogOwner = makeLoggerFromCommandLine();
35006c3fb27SDimitry Andric       this->Opts.Log = LogOwner.get();
35106c3fb27SDimitry Andric       // FIXME: if the flag is given a value, write an HTML log to a file.
35206c3fb27SDimitry Andric     } else {
35306c3fb27SDimitry Andric       this->Opts.Log = &Logger::null();
35406c3fb27SDimitry Andric     }
35506c3fb27SDimitry Andric   }
35606c3fb27SDimitry Andric }
35706c3fb27SDimitry Andric 
35806c3fb27SDimitry Andric DataflowAnalysisContext::~DataflowAnalysisContext() = default;
35906c3fb27SDimitry Andric 
36081ad6265SDimitry Andric } // namespace dataflow
36181ad6265SDimitry Andric } // namespace clang
36281ad6265SDimitry Andric 
36381ad6265SDimitry Andric using namespace clang;
36481ad6265SDimitry Andric 
36581ad6265SDimitry Andric const Expr &clang::dataflow::ignoreCFGOmittedNodes(const Expr &E) {
36681ad6265SDimitry Andric   const Expr *Current = &E;
36781ad6265SDimitry Andric   if (auto *EWC = dyn_cast<ExprWithCleanups>(Current)) {
36881ad6265SDimitry Andric     Current = EWC->getSubExpr();
36981ad6265SDimitry Andric     assert(Current != nullptr);
37081ad6265SDimitry Andric   }
37181ad6265SDimitry Andric   Current = Current->IgnoreParens();
37281ad6265SDimitry Andric   assert(Current != nullptr);
37381ad6265SDimitry Andric   return *Current;
37481ad6265SDimitry Andric }
37581ad6265SDimitry Andric 
37681ad6265SDimitry Andric const Stmt &clang::dataflow::ignoreCFGOmittedNodes(const Stmt &S) {
37781ad6265SDimitry Andric   if (auto *E = dyn_cast<Expr>(&S))
37881ad6265SDimitry Andric     return ignoreCFGOmittedNodes(*E);
37981ad6265SDimitry Andric   return S;
38081ad6265SDimitry Andric }
38181ad6265SDimitry Andric 
38281ad6265SDimitry Andric // FIXME: Does not precisely handle non-virtual diamond inheritance. A single
38381ad6265SDimitry Andric // field decl will be modeled for all instances of the inherited field.
38406c3fb27SDimitry Andric static void getFieldsFromClassHierarchy(QualType Type,
38506c3fb27SDimitry Andric                                         clang::dataflow::FieldSet &Fields) {
38681ad6265SDimitry Andric   if (Type->isIncompleteType() || Type->isDependentType() ||
38781ad6265SDimitry Andric       !Type->isRecordType())
38881ad6265SDimitry Andric     return;
38981ad6265SDimitry Andric 
39081ad6265SDimitry Andric   for (const FieldDecl *Field : Type->getAsRecordDecl()->fields())
39181ad6265SDimitry Andric     Fields.insert(Field);
39281ad6265SDimitry Andric   if (auto *CXXRecord = Type->getAsCXXRecordDecl())
39381ad6265SDimitry Andric     for (const CXXBaseSpecifier &Base : CXXRecord->bases())
39481ad6265SDimitry Andric       getFieldsFromClassHierarchy(Base.getType(), Fields);
39581ad6265SDimitry Andric }
39681ad6265SDimitry Andric 
39781ad6265SDimitry Andric /// Gets the set of all fields in the type.
39806c3fb27SDimitry Andric clang::dataflow::FieldSet clang::dataflow::getObjectFields(QualType Type) {
39906c3fb27SDimitry Andric   FieldSet Fields;
40081ad6265SDimitry Andric   getFieldsFromClassHierarchy(Type, Fields);
40181ad6265SDimitry Andric   return Fields;
40281ad6265SDimitry Andric }
4035f757f3fSDimitry Andric 
4045f757f3fSDimitry Andric bool clang::dataflow::containsSameFields(
4055f757f3fSDimitry Andric     const clang::dataflow::FieldSet &Fields,
4065f757f3fSDimitry Andric     const clang::dataflow::RecordStorageLocation::FieldToLoc &FieldLocs) {
4075f757f3fSDimitry Andric   if (Fields.size() != FieldLocs.size())
4085f757f3fSDimitry Andric     return false;
4095f757f3fSDimitry Andric   for ([[maybe_unused]] auto [Field, Loc] : FieldLocs)
4105f757f3fSDimitry Andric     if (!Fields.contains(cast_or_null<FieldDecl>(Field)))
4115f757f3fSDimitry Andric       return false;
4125f757f3fSDimitry Andric   return true;
4135f757f3fSDimitry Andric }
414