159ff3adcSmartinboehme //===- AdornedCFG.cpp ---------------------------------------------===// 259ff3adcSmartinboehme // 359ff3adcSmartinboehme // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 459ff3adcSmartinboehme // See https://llvm.org/LICENSE.txt for license information. 559ff3adcSmartinboehme // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 659ff3adcSmartinboehme // 759ff3adcSmartinboehme //===----------------------------------------------------------------------===// 859ff3adcSmartinboehme // 959ff3adcSmartinboehme // This file defines an `AdornedCFG` class that is used by dataflow analyses 1059ff3adcSmartinboehme // that run over Control-Flow Graphs (CFGs). 1159ff3adcSmartinboehme // 1259ff3adcSmartinboehme //===----------------------------------------------------------------------===// 1359ff3adcSmartinboehme 1459ff3adcSmartinboehme #include "clang/Analysis/FlowSensitive/AdornedCFG.h" 1559ff3adcSmartinboehme #include "clang/AST/ASTContext.h" 1659ff3adcSmartinboehme #include "clang/AST/Decl.h" 1759ff3adcSmartinboehme #include "clang/AST/Stmt.h" 1859ff3adcSmartinboehme #include "clang/Analysis/CFG.h" 19*0362a299Smartinboehme #include "clang/Analysis/FlowSensitive/ASTOps.h" 2059ff3adcSmartinboehme #include "llvm/ADT/BitVector.h" 2159ff3adcSmartinboehme #include "llvm/ADT/DenseMap.h" 2259ff3adcSmartinboehme #include "llvm/Support/Error.h" 2359ff3adcSmartinboehme #include <utility> 2459ff3adcSmartinboehme 2559ff3adcSmartinboehme namespace clang { 2659ff3adcSmartinboehme namespace dataflow { 2759ff3adcSmartinboehme 2859ff3adcSmartinboehme /// Returns a map from statements to basic blocks that contain them. 2959ff3adcSmartinboehme static llvm::DenseMap<const Stmt *, const CFGBlock *> 3059ff3adcSmartinboehme buildStmtToBasicBlockMap(const CFG &Cfg) { 3159ff3adcSmartinboehme llvm::DenseMap<const Stmt *, const CFGBlock *> StmtToBlock; 3259ff3adcSmartinboehme for (const CFGBlock *Block : Cfg) { 3359ff3adcSmartinboehme if (Block == nullptr) 3459ff3adcSmartinboehme continue; 3559ff3adcSmartinboehme 3659ff3adcSmartinboehme for (const CFGElement &Element : *Block) { 3759ff3adcSmartinboehme auto Stmt = Element.getAs<CFGStmt>(); 3859ff3adcSmartinboehme if (!Stmt) 3959ff3adcSmartinboehme continue; 4059ff3adcSmartinboehme 4159ff3adcSmartinboehme StmtToBlock[Stmt->getStmt()] = Block; 4259ff3adcSmartinboehme } 4359ff3adcSmartinboehme } 4459ff3adcSmartinboehme // Some terminator conditions don't appear as a `CFGElement` anywhere else - 4559ff3adcSmartinboehme // for example, this is true if the terminator condition is a `&&` or `||` 4659ff3adcSmartinboehme // operator. 4759ff3adcSmartinboehme // We associate these conditions with the block the terminator appears in, 4859ff3adcSmartinboehme // but only if the condition has not already appeared as a regular 4959ff3adcSmartinboehme // `CFGElement`. (The `insert()` below does nothing if the key already exists 5059ff3adcSmartinboehme // in the map.) 5159ff3adcSmartinboehme for (const CFGBlock *Block : Cfg) { 5259ff3adcSmartinboehme if (Block != nullptr) 5359ff3adcSmartinboehme if (const Stmt *TerminatorCond = Block->getTerminatorCondition()) 5459ff3adcSmartinboehme StmtToBlock.insert({TerminatorCond, Block}); 5559ff3adcSmartinboehme } 5659ff3adcSmartinboehme // Terminator statements typically don't appear as a `CFGElement` anywhere 5759ff3adcSmartinboehme // else, so we want to associate them with the block that they terminate. 5859ff3adcSmartinboehme // However, there are some important special cases: 5959ff3adcSmartinboehme // - The conditional operator is a type of terminator, but it also appears 6059ff3adcSmartinboehme // as a regular `CFGElement`, and we want to associate it with the block 6159ff3adcSmartinboehme // in which it appears as a `CFGElement`. 6259ff3adcSmartinboehme // - The `&&` and `||` operators are types of terminators, but like the 6359ff3adcSmartinboehme // conditional operator, they can appear as a regular `CFGElement` or 6459ff3adcSmartinboehme // as a terminator condition (see above). 6559ff3adcSmartinboehme // We process terminators last to make sure that we only associate them with 6659ff3adcSmartinboehme // the block they terminate if they haven't previously occurred as a regular 6759ff3adcSmartinboehme // `CFGElement` or as a terminator condition. 6859ff3adcSmartinboehme for (const CFGBlock *Block : Cfg) { 6959ff3adcSmartinboehme if (Block != nullptr) 7059ff3adcSmartinboehme if (const Stmt *TerminatorStmt = Block->getTerminatorStmt()) 7159ff3adcSmartinboehme StmtToBlock.insert({TerminatorStmt, Block}); 7259ff3adcSmartinboehme } 7359ff3adcSmartinboehme return StmtToBlock; 7459ff3adcSmartinboehme } 7559ff3adcSmartinboehme 7659ff3adcSmartinboehme static llvm::BitVector findReachableBlocks(const CFG &Cfg) { 7759ff3adcSmartinboehme llvm::BitVector BlockReachable(Cfg.getNumBlockIDs(), false); 7859ff3adcSmartinboehme 7959ff3adcSmartinboehme llvm::SmallVector<const CFGBlock *> BlocksToVisit; 8059ff3adcSmartinboehme BlocksToVisit.push_back(&Cfg.getEntry()); 8159ff3adcSmartinboehme while (!BlocksToVisit.empty()) { 8259ff3adcSmartinboehme const CFGBlock *Block = BlocksToVisit.back(); 8359ff3adcSmartinboehme BlocksToVisit.pop_back(); 8459ff3adcSmartinboehme 8559ff3adcSmartinboehme if (BlockReachable[Block->getBlockID()]) 8659ff3adcSmartinboehme continue; 8759ff3adcSmartinboehme 8859ff3adcSmartinboehme BlockReachable[Block->getBlockID()] = true; 8959ff3adcSmartinboehme 9059ff3adcSmartinboehme for (const CFGBlock *Succ : Block->succs()) 9159ff3adcSmartinboehme if (Succ) 9259ff3adcSmartinboehme BlocksToVisit.push_back(Succ); 9359ff3adcSmartinboehme } 9459ff3adcSmartinboehme 9559ff3adcSmartinboehme return BlockReachable; 9659ff3adcSmartinboehme } 9759ff3adcSmartinboehme 9859ff3adcSmartinboehme static llvm::DenseSet<const CFGBlock *> 9959ff3adcSmartinboehme buildContainsExprConsumedInDifferentBlock( 100*0362a299Smartinboehme const CFG &Cfg, const internal::StmtToBlockMap &StmtToBlock) { 10159ff3adcSmartinboehme llvm::DenseSet<const CFGBlock *> Result; 10259ff3adcSmartinboehme 10359ff3adcSmartinboehme auto CheckChildExprs = [&Result, &StmtToBlock](const Stmt *S, 10459ff3adcSmartinboehme const CFGBlock *Block) { 10559ff3adcSmartinboehme for (const Stmt *Child : S->children()) { 106a6a60662SEric Li if (!isa_and_nonnull<Expr>(Child)) 10759ff3adcSmartinboehme continue; 108*0362a299Smartinboehme const CFGBlock *ChildBlock = StmtToBlock.lookup(*Child); 10959ff3adcSmartinboehme if (ChildBlock != Block) 11059ff3adcSmartinboehme Result.insert(ChildBlock); 11159ff3adcSmartinboehme } 11259ff3adcSmartinboehme }; 11359ff3adcSmartinboehme 11459ff3adcSmartinboehme for (const CFGBlock *Block : Cfg) { 11559ff3adcSmartinboehme if (Block == nullptr) 11659ff3adcSmartinboehme continue; 11759ff3adcSmartinboehme 11859ff3adcSmartinboehme for (const CFGElement &Element : *Block) 11959ff3adcSmartinboehme if (auto S = Element.getAs<CFGStmt>()) 12059ff3adcSmartinboehme CheckChildExprs(S->getStmt(), Block); 12159ff3adcSmartinboehme 12259ff3adcSmartinboehme if (const Stmt *TerminatorCond = Block->getTerminatorCondition()) 12359ff3adcSmartinboehme CheckChildExprs(TerminatorCond, Block); 12459ff3adcSmartinboehme } 12559ff3adcSmartinboehme 12659ff3adcSmartinboehme return Result; 12759ff3adcSmartinboehme } 12859ff3adcSmartinboehme 129*0362a299Smartinboehme namespace internal { 130*0362a299Smartinboehme 131*0362a299Smartinboehme StmtToBlockMap::StmtToBlockMap(const CFG &Cfg) 132*0362a299Smartinboehme : StmtToBlock(buildStmtToBasicBlockMap(Cfg)) {} 133*0362a299Smartinboehme 134*0362a299Smartinboehme } // namespace internal 135*0362a299Smartinboehme 13659ff3adcSmartinboehme llvm::Expected<AdornedCFG> AdornedCFG::build(const FunctionDecl &Func) { 13759ff3adcSmartinboehme if (!Func.doesThisDeclarationHaveABody()) 13859ff3adcSmartinboehme return llvm::createStringError( 13959ff3adcSmartinboehme std::make_error_code(std::errc::invalid_argument), 14059ff3adcSmartinboehme "Cannot analyze function without a body"); 14159ff3adcSmartinboehme 14259ff3adcSmartinboehme return build(Func, *Func.getBody(), Func.getASTContext()); 14359ff3adcSmartinboehme } 14459ff3adcSmartinboehme 14559ff3adcSmartinboehme llvm::Expected<AdornedCFG> AdornedCFG::build(const Decl &D, Stmt &S, 14659ff3adcSmartinboehme ASTContext &C) { 14759ff3adcSmartinboehme if (D.isTemplated()) 14859ff3adcSmartinboehme return llvm::createStringError( 14959ff3adcSmartinboehme std::make_error_code(std::errc::invalid_argument), 15059ff3adcSmartinboehme "Cannot analyze templated declarations"); 15159ff3adcSmartinboehme 15259ff3adcSmartinboehme // The shape of certain elements of the AST can vary depending on the 15359ff3adcSmartinboehme // language. We currently only support C++. 154e6f63a94Smartinboehme if (!C.getLangOpts().CPlusPlus || C.getLangOpts().ObjC) 15559ff3adcSmartinboehme return llvm::createStringError( 15659ff3adcSmartinboehme std::make_error_code(std::errc::invalid_argument), 15759ff3adcSmartinboehme "Can only analyze C++"); 15859ff3adcSmartinboehme 15959ff3adcSmartinboehme CFG::BuildOptions Options; 16059ff3adcSmartinboehme Options.PruneTriviallyFalseEdges = true; 16159ff3adcSmartinboehme Options.AddImplicitDtors = true; 16259ff3adcSmartinboehme Options.AddTemporaryDtors = true; 16359ff3adcSmartinboehme Options.AddInitializers = true; 16459ff3adcSmartinboehme Options.AddCXXDefaultInitExprInCtors = true; 16559ff3adcSmartinboehme Options.AddLifetime = true; 16659ff3adcSmartinboehme 16759ff3adcSmartinboehme // Ensure that all sub-expressions in basic blocks are evaluated. 16859ff3adcSmartinboehme Options.setAllAlwaysAdd(); 16959ff3adcSmartinboehme 17059ff3adcSmartinboehme auto Cfg = CFG::buildCFG(&D, &S, &C, Options); 17159ff3adcSmartinboehme if (Cfg == nullptr) 17259ff3adcSmartinboehme return llvm::createStringError( 17359ff3adcSmartinboehme std::make_error_code(std::errc::invalid_argument), 17459ff3adcSmartinboehme "CFG::buildCFG failed"); 17559ff3adcSmartinboehme 176*0362a299Smartinboehme internal::StmtToBlockMap StmtToBlock(*Cfg); 17759ff3adcSmartinboehme 17859ff3adcSmartinboehme llvm::BitVector BlockReachable = findReachableBlocks(*Cfg); 17959ff3adcSmartinboehme 18059ff3adcSmartinboehme llvm::DenseSet<const CFGBlock *> ContainsExprConsumedInDifferentBlock = 18159ff3adcSmartinboehme buildContainsExprConsumedInDifferentBlock(*Cfg, StmtToBlock); 18259ff3adcSmartinboehme 18359ff3adcSmartinboehme return AdornedCFG(D, std::move(Cfg), std::move(StmtToBlock), 18459ff3adcSmartinboehme std::move(BlockReachable), 18559ff3adcSmartinboehme std::move(ContainsExprConsumedInDifferentBlock)); 18659ff3adcSmartinboehme } 18759ff3adcSmartinboehme 18859ff3adcSmartinboehme } // namespace dataflow 18959ff3adcSmartinboehme } // namespace clang 190