1 //===- AdornedCFG.cpp ---------------------------------------------===// 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 an `AdornedCFG` class that is used by dataflow analyses 10 // that run over Control-Flow Graphs (CFGs). 11 // 12 //===----------------------------------------------------------------------===// 13 14 #include "clang/Analysis/FlowSensitive/AdornedCFG.h" 15 #include "clang/AST/ASTContext.h" 16 #include "clang/AST/Decl.h" 17 #include "clang/AST/Stmt.h" 18 #include "clang/Analysis/CFG.h" 19 #include "llvm/ADT/BitVector.h" 20 #include "llvm/ADT/DenseMap.h" 21 #include "llvm/Support/Error.h" 22 #include <utility> 23 24 namespace clang { 25 namespace dataflow { 26 27 /// Returns a map from statements to basic blocks that contain them. 28 static llvm::DenseMap<const Stmt *, const CFGBlock *> 29 buildStmtToBasicBlockMap(const CFG &Cfg) { 30 llvm::DenseMap<const Stmt *, const CFGBlock *> StmtToBlock; 31 for (const CFGBlock *Block : Cfg) { 32 if (Block == nullptr) 33 continue; 34 35 for (const CFGElement &Element : *Block) { 36 auto Stmt = Element.getAs<CFGStmt>(); 37 if (!Stmt) 38 continue; 39 40 StmtToBlock[Stmt->getStmt()] = Block; 41 } 42 } 43 // Some terminator conditions don't appear as a `CFGElement` anywhere else - 44 // for example, this is true if the terminator condition is a `&&` or `||` 45 // operator. 46 // We associate these conditions with the block the terminator appears in, 47 // but only if the condition has not already appeared as a regular 48 // `CFGElement`. (The `insert()` below does nothing if the key already exists 49 // in the map.) 50 for (const CFGBlock *Block : Cfg) { 51 if (Block != nullptr) 52 if (const Stmt *TerminatorCond = Block->getTerminatorCondition()) 53 StmtToBlock.insert({TerminatorCond, Block}); 54 } 55 // Terminator statements typically don't appear as a `CFGElement` anywhere 56 // else, so we want to associate them with the block that they terminate. 57 // However, there are some important special cases: 58 // - The conditional operator is a type of terminator, but it also appears 59 // as a regular `CFGElement`, and we want to associate it with the block 60 // in which it appears as a `CFGElement`. 61 // - The `&&` and `||` operators are types of terminators, but like the 62 // conditional operator, they can appear as a regular `CFGElement` or 63 // as a terminator condition (see above). 64 // We process terminators last to make sure that we only associate them with 65 // the block they terminate if they haven't previously occurred as a regular 66 // `CFGElement` or as a terminator condition. 67 for (const CFGBlock *Block : Cfg) { 68 if (Block != nullptr) 69 if (const Stmt *TerminatorStmt = Block->getTerminatorStmt()) 70 StmtToBlock.insert({TerminatorStmt, Block}); 71 } 72 return StmtToBlock; 73 } 74 75 static llvm::BitVector findReachableBlocks(const CFG &Cfg) { 76 llvm::BitVector BlockReachable(Cfg.getNumBlockIDs(), false); 77 78 llvm::SmallVector<const CFGBlock *> BlocksToVisit; 79 BlocksToVisit.push_back(&Cfg.getEntry()); 80 while (!BlocksToVisit.empty()) { 81 const CFGBlock *Block = BlocksToVisit.back(); 82 BlocksToVisit.pop_back(); 83 84 if (BlockReachable[Block->getBlockID()]) 85 continue; 86 87 BlockReachable[Block->getBlockID()] = true; 88 89 for (const CFGBlock *Succ : Block->succs()) 90 if (Succ) 91 BlocksToVisit.push_back(Succ); 92 } 93 94 return BlockReachable; 95 } 96 97 static llvm::DenseSet<const CFGBlock *> 98 buildContainsExprConsumedInDifferentBlock( 99 const CFG &Cfg, const internal::StmtToBlockMap &StmtToBlock) { 100 llvm::DenseSet<const CFGBlock *> Result; 101 102 auto CheckChildExprs = [&Result, &StmtToBlock](const Stmt *S, 103 const CFGBlock *Block) { 104 for (const Stmt *Child : S->children()) { 105 if (!isa_and_nonnull<Expr>(Child)) 106 continue; 107 const CFGBlock *ChildBlock = StmtToBlock.lookup(*Child); 108 if (ChildBlock != Block) 109 Result.insert(ChildBlock); 110 } 111 }; 112 113 for (const CFGBlock *Block : Cfg) { 114 if (Block == nullptr) 115 continue; 116 117 for (const CFGElement &Element : *Block) 118 if (auto S = Element.getAs<CFGStmt>()) 119 CheckChildExprs(S->getStmt(), Block); 120 121 if (const Stmt *TerminatorCond = Block->getTerminatorCondition()) 122 CheckChildExprs(TerminatorCond, Block); 123 } 124 125 return Result; 126 } 127 128 namespace internal { 129 130 StmtToBlockMap::StmtToBlockMap(const CFG &Cfg) 131 : StmtToBlock(buildStmtToBasicBlockMap(Cfg)) {} 132 133 } // namespace internal 134 135 llvm::Expected<AdornedCFG> AdornedCFG::build(const FunctionDecl &Func) { 136 if (!Func.doesThisDeclarationHaveABody()) 137 return llvm::createStringError( 138 std::make_error_code(std::errc::invalid_argument), 139 "Cannot analyze function without a body"); 140 141 return build(Func, *Func.getBody(), Func.getASTContext()); 142 } 143 144 llvm::Expected<AdornedCFG> AdornedCFG::build(const Decl &D, Stmt &S, 145 ASTContext &C) { 146 if (D.isTemplated()) 147 return llvm::createStringError( 148 std::make_error_code(std::errc::invalid_argument), 149 "Cannot analyze templated declarations"); 150 151 // The shape of certain elements of the AST can vary depending on the 152 // language. We currently only support C++. 153 if (!C.getLangOpts().CPlusPlus || C.getLangOpts().ObjC) 154 return llvm::createStringError( 155 std::make_error_code(std::errc::invalid_argument), 156 "Can only analyze C++"); 157 158 CFG::BuildOptions Options; 159 Options.PruneTriviallyFalseEdges = true; 160 Options.AddImplicitDtors = true; 161 Options.AddTemporaryDtors = true; 162 Options.AddInitializers = true; 163 Options.AddCXXDefaultInitExprInCtors = true; 164 Options.AddLifetime = true; 165 166 // Ensure that all sub-expressions in basic blocks are evaluated. 167 Options.setAllAlwaysAdd(); 168 169 auto Cfg = CFG::buildCFG(&D, &S, &C, Options); 170 if (Cfg == nullptr) 171 return llvm::createStringError( 172 std::make_error_code(std::errc::invalid_argument), 173 "CFG::buildCFG failed"); 174 175 internal::StmtToBlockMap StmtToBlock(*Cfg); 176 177 llvm::BitVector BlockReachable = findReachableBlocks(*Cfg); 178 179 llvm::DenseSet<const CFGBlock *> ContainsExprConsumedInDifferentBlock = 180 buildContainsExprConsumedInDifferentBlock(*Cfg, StmtToBlock); 181 182 return AdornedCFG(D, std::move(Cfg), std::move(StmtToBlock), 183 std::move(BlockReachable), 184 std::move(ContainsExprConsumedInDifferentBlock)); 185 } 186 187 } // namespace dataflow 188 } // namespace clang 189