1 //===-- DataflowAnalysisContext.cpp -----------------------------*- 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 a DataflowAnalysisContext class that owns objects that 10 // encompass the state of a program and stores context that is used during 11 // dataflow analysis. 12 // 13 //===----------------------------------------------------------------------===// 14 15 #include "clang/Analysis/FlowSensitive/DataflowAnalysisContext.h" 16 #include "clang/AST/ExprCXX.h" 17 #include "clang/Analysis/FlowSensitive/Value.h" 18 #include <cassert> 19 #include <memory> 20 #include <utility> 21 22 namespace clang { 23 namespace dataflow { 24 25 StorageLocation & 26 DataflowAnalysisContext::getStableStorageLocation(QualType Type) { 27 assert(!Type.isNull()); 28 if (Type->isStructureOrClassType() || Type->isUnionType()) { 29 // FIXME: Explore options to avoid eager initialization of fields as some of 30 // them might not be needed for a particular analysis. 31 llvm::DenseMap<const ValueDecl *, StorageLocation *> FieldLocs; 32 for (const FieldDecl *Field : getObjectFields(Type)) 33 FieldLocs.insert({Field, &getStableStorageLocation(Field->getType())}); 34 return takeOwnership( 35 std::make_unique<AggregateStorageLocation>(Type, std::move(FieldLocs))); 36 } 37 return takeOwnership(std::make_unique<ScalarStorageLocation>(Type)); 38 } 39 40 StorageLocation & 41 DataflowAnalysisContext::getStableStorageLocation(const VarDecl &D) { 42 if (auto *Loc = getStorageLocation(D)) 43 return *Loc; 44 auto &Loc = getStableStorageLocation(D.getType()); 45 setStorageLocation(D, Loc); 46 return Loc; 47 } 48 49 StorageLocation & 50 DataflowAnalysisContext::getStableStorageLocation(const Expr &E) { 51 if (auto *Loc = getStorageLocation(E)) 52 return *Loc; 53 auto &Loc = getStableStorageLocation(E.getType()); 54 setStorageLocation(E, Loc); 55 return Loc; 56 } 57 58 static std::pair<BoolValue *, BoolValue *> 59 makeCanonicalBoolValuePair(BoolValue &LHS, BoolValue &RHS) { 60 auto Res = std::make_pair(&LHS, &RHS); 61 if (&RHS < &LHS) 62 std::swap(Res.first, Res.second); 63 return Res; 64 } 65 66 BoolValue &DataflowAnalysisContext::getOrCreateConjunction(BoolValue &LHS, 67 BoolValue &RHS) { 68 if (&LHS == &RHS) 69 return LHS; 70 71 auto Res = ConjunctionVals.try_emplace(makeCanonicalBoolValuePair(LHS, RHS), 72 nullptr); 73 if (Res.second) 74 Res.first->second = 75 &takeOwnership(std::make_unique<ConjunctionValue>(LHS, RHS)); 76 return *Res.first->second; 77 } 78 79 BoolValue &DataflowAnalysisContext::getOrCreateDisjunction(BoolValue &LHS, 80 BoolValue &RHS) { 81 if (&LHS == &RHS) 82 return LHS; 83 84 auto Res = DisjunctionVals.try_emplace(makeCanonicalBoolValuePair(LHS, RHS), 85 nullptr); 86 if (Res.second) 87 Res.first->second = 88 &takeOwnership(std::make_unique<DisjunctionValue>(LHS, RHS)); 89 return *Res.first->second; 90 } 91 92 BoolValue &DataflowAnalysisContext::getOrCreateNegation(BoolValue &Val) { 93 auto Res = NegationVals.try_emplace(&Val, nullptr); 94 if (Res.second) 95 Res.first->second = &takeOwnership(std::make_unique<NegationValue>(Val)); 96 return *Res.first->second; 97 } 98 99 BoolValue &DataflowAnalysisContext::getOrCreateImplication(BoolValue &LHS, 100 BoolValue &RHS) { 101 return &LHS == &RHS ? getBoolLiteralValue(true) 102 : getOrCreateDisjunction(getOrCreateNegation(LHS), RHS); 103 } 104 105 BoolValue &DataflowAnalysisContext::getOrCreateIff(BoolValue &LHS, 106 BoolValue &RHS) { 107 return &LHS == &RHS 108 ? getBoolLiteralValue(true) 109 : getOrCreateConjunction(getOrCreateImplication(LHS, RHS), 110 getOrCreateImplication(RHS, LHS)); 111 } 112 113 AtomicBoolValue &DataflowAnalysisContext::makeFlowConditionToken() { 114 return createAtomicBoolValue(); 115 } 116 117 void DataflowAnalysisContext::addFlowConditionConstraint( 118 AtomicBoolValue &Token, BoolValue &Constraint) { 119 auto Res = FlowConditionConstraints.try_emplace(&Token, &Constraint); 120 if (!Res.second) { 121 Res.first->second = &getOrCreateConjunction(*Res.first->second, Constraint); 122 } 123 } 124 125 AtomicBoolValue & 126 DataflowAnalysisContext::forkFlowCondition(AtomicBoolValue &Token) { 127 auto &ForkToken = makeFlowConditionToken(); 128 FlowConditionDeps[&ForkToken].insert(&Token); 129 addFlowConditionConstraint(ForkToken, Token); 130 return ForkToken; 131 } 132 133 AtomicBoolValue & 134 DataflowAnalysisContext::joinFlowConditions(AtomicBoolValue &FirstToken, 135 AtomicBoolValue &SecondToken) { 136 auto &Token = makeFlowConditionToken(); 137 FlowConditionDeps[&Token].insert(&FirstToken); 138 FlowConditionDeps[&Token].insert(&SecondToken); 139 addFlowConditionConstraint(Token, 140 getOrCreateDisjunction(FirstToken, SecondToken)); 141 return Token; 142 } 143 144 Solver::Result 145 DataflowAnalysisContext::querySolver(llvm::DenseSet<BoolValue *> Constraints) { 146 Constraints.insert(&getBoolLiteralValue(true)); 147 Constraints.insert(&getOrCreateNegation(getBoolLiteralValue(false))); 148 return S->solve(std::move(Constraints)); 149 } 150 151 bool DataflowAnalysisContext::flowConditionImplies(AtomicBoolValue &Token, 152 BoolValue &Val) { 153 // Returns true if and only if truth assignment of the flow condition implies 154 // that `Val` is also true. We prove whether or not this property holds by 155 // reducing the problem to satisfiability checking. In other words, we attempt 156 // to show that assuming `Val` is false makes the constraints induced by the 157 // flow condition unsatisfiable. 158 llvm::DenseSet<BoolValue *> Constraints = {&Token, &getOrCreateNegation(Val)}; 159 llvm::DenseSet<AtomicBoolValue *> VisitedTokens; 160 addTransitiveFlowConditionConstraints(Token, Constraints, VisitedTokens); 161 return isUnsatisfiable(std::move(Constraints)); 162 } 163 164 bool DataflowAnalysisContext::flowConditionIsTautology(AtomicBoolValue &Token) { 165 // Returns true if and only if we cannot prove that the flow condition can 166 // ever be false. 167 llvm::DenseSet<BoolValue *> Constraints = {&getOrCreateNegation(Token)}; 168 llvm::DenseSet<AtomicBoolValue *> VisitedTokens; 169 addTransitiveFlowConditionConstraints(Token, Constraints, VisitedTokens); 170 return isUnsatisfiable(std::move(Constraints)); 171 } 172 173 bool DataflowAnalysisContext::equivalentBoolValues(BoolValue &Val1, 174 BoolValue &Val2) { 175 llvm::DenseSet<BoolValue *> Constraints = { 176 &getOrCreateNegation(getOrCreateIff(Val1, Val2))}; 177 return isUnsatisfiable(Constraints); 178 } 179 180 void DataflowAnalysisContext::addTransitiveFlowConditionConstraints( 181 AtomicBoolValue &Token, llvm::DenseSet<BoolValue *> &Constraints, 182 llvm::DenseSet<AtomicBoolValue *> &VisitedTokens) { 183 auto Res = VisitedTokens.insert(&Token); 184 if (!Res.second) 185 return; 186 187 auto ConstraintsIT = FlowConditionConstraints.find(&Token); 188 if (ConstraintsIT == FlowConditionConstraints.end()) { 189 Constraints.insert(&Token); 190 } else { 191 // Bind flow condition token via `iff` to its set of constraints: 192 // FC <=> (C1 ^ C2 ^ ...), where Ci are constraints 193 Constraints.insert(&getOrCreateIff(Token, *ConstraintsIT->second)); 194 } 195 196 auto DepsIT = FlowConditionDeps.find(&Token); 197 if (DepsIT != FlowConditionDeps.end()) { 198 for (AtomicBoolValue *DepToken : DepsIT->second) { 199 addTransitiveFlowConditionConstraints(*DepToken, Constraints, 200 VisitedTokens); 201 } 202 } 203 } 204 205 BoolValue &DataflowAnalysisContext::substituteBoolValue( 206 BoolValue &Val, 207 llvm::DenseMap<BoolValue *, BoolValue *> &SubstitutionsCache) { 208 auto IT = SubstitutionsCache.find(&Val); 209 if (IT != SubstitutionsCache.end()) { 210 return *IT->second; 211 } 212 BoolValue *Result; 213 switch (Val.getKind()) { 214 case Value::Kind::AtomicBool: { 215 Result = &Val; 216 break; 217 } 218 case Value::Kind::Negation: { 219 auto &Negation = *cast<NegationValue>(&Val); 220 auto &Sub = substituteBoolValue(Negation.getSubVal(), SubstitutionsCache); 221 Result = &getOrCreateNegation(Sub); 222 break; 223 } 224 case Value::Kind::Disjunction: { 225 auto &Disjunct = *cast<DisjunctionValue>(&Val); 226 auto &LeftSub = 227 substituteBoolValue(Disjunct.getLeftSubValue(), SubstitutionsCache); 228 auto &RightSub = 229 substituteBoolValue(Disjunct.getRightSubValue(), SubstitutionsCache); 230 Result = &getOrCreateDisjunction(LeftSub, RightSub); 231 break; 232 } 233 case Value::Kind::Conjunction: { 234 auto &Conjunct = *cast<ConjunctionValue>(&Val); 235 auto &LeftSub = 236 substituteBoolValue(Conjunct.getLeftSubValue(), SubstitutionsCache); 237 auto &RightSub = 238 substituteBoolValue(Conjunct.getRightSubValue(), SubstitutionsCache); 239 Result = &getOrCreateConjunction(LeftSub, RightSub); 240 break; 241 } 242 default: 243 llvm_unreachable("Unhandled Value Kind"); 244 } 245 SubstitutionsCache[&Val] = Result; 246 return *Result; 247 } 248 249 BoolValue &DataflowAnalysisContext::buildAndSubstituteFlowCondition( 250 AtomicBoolValue &Token, 251 llvm::DenseMap<AtomicBoolValue *, BoolValue *> Substitutions) { 252 llvm::DenseMap<BoolValue *, BoolValue *> SubstitutionsCache( 253 Substitutions.begin(), Substitutions.end()); 254 return buildAndSubstituteFlowConditionWithCache(Token, SubstitutionsCache); 255 } 256 257 BoolValue &DataflowAnalysisContext::buildAndSubstituteFlowConditionWithCache( 258 AtomicBoolValue &Token, 259 llvm::DenseMap<BoolValue *, BoolValue *> &SubstitutionsCache) { 260 auto ConstraintsIT = FlowConditionConstraints.find(&Token); 261 if (ConstraintsIT == FlowConditionConstraints.end()) { 262 return getBoolLiteralValue(true); 263 } 264 auto DepsIT = FlowConditionDeps.find(&Token); 265 if (DepsIT != FlowConditionDeps.end()) { 266 for (AtomicBoolValue *DepToken : DepsIT->second) { 267 auto &NewDep = buildAndSubstituteFlowConditionWithCache( 268 *DepToken, SubstitutionsCache); 269 SubstitutionsCache[DepToken] = &NewDep; 270 } 271 } 272 return substituteBoolValue(*ConstraintsIT->second, SubstitutionsCache); 273 } 274 275 } // namespace dataflow 276 } // namespace clang 277 278 using namespace clang; 279 280 const Expr &clang::dataflow::ignoreCFGOmittedNodes(const Expr &E) { 281 const Expr *Current = &E; 282 if (auto *EWC = dyn_cast<ExprWithCleanups>(Current)) { 283 Current = EWC->getSubExpr(); 284 assert(Current != nullptr); 285 } 286 Current = Current->IgnoreParens(); 287 assert(Current != nullptr); 288 return *Current; 289 } 290 291 const Stmt &clang::dataflow::ignoreCFGOmittedNodes(const Stmt &S) { 292 if (auto *E = dyn_cast<Expr>(&S)) 293 return ignoreCFGOmittedNodes(*E); 294 return S; 295 } 296 297 // FIXME: Does not precisely handle non-virtual diamond inheritance. A single 298 // field decl will be modeled for all instances of the inherited field. 299 static void 300 getFieldsFromClassHierarchy(QualType Type, 301 llvm::DenseSet<const FieldDecl *> &Fields) { 302 if (Type->isIncompleteType() || Type->isDependentType() || 303 !Type->isRecordType()) 304 return; 305 306 for (const FieldDecl *Field : Type->getAsRecordDecl()->fields()) 307 Fields.insert(Field); 308 if (auto *CXXRecord = Type->getAsCXXRecordDecl()) 309 for (const CXXBaseSpecifier &Base : CXXRecord->bases()) 310 getFieldsFromClassHierarchy(Base.getType(), Fields); 311 } 312 313 /// Gets the set of all fields in the type. 314 llvm::DenseSet<const FieldDecl *> 315 clang::dataflow::getObjectFields(QualType Type) { 316 llvm::DenseSet<const FieldDecl *> Fields; 317 getFieldsFromClassHierarchy(Type, Fields); 318 return Fields; 319 } 320