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