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