Path: blob/main/contrib/llvm-project/clang/lib/Analysis/FlowSensitive/AdornedCFG.cpp
35269 views
//===- AdornedCFG.cpp ---------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This file defines an `AdornedCFG` class that is used by dataflow analyses9// that run over Control-Flow Graphs (CFGs).10//11//===----------------------------------------------------------------------===//1213#include "clang/Analysis/FlowSensitive/AdornedCFG.h"14#include "clang/AST/ASTContext.h"15#include "clang/AST/Decl.h"16#include "clang/AST/Stmt.h"17#include "clang/Analysis/CFG.h"18#include "llvm/ADT/BitVector.h"19#include "llvm/ADT/DenseMap.h"20#include "llvm/Support/Error.h"21#include <utility>2223namespace clang {24namespace dataflow {2526/// Returns a map from statements to basic blocks that contain them.27static llvm::DenseMap<const Stmt *, const CFGBlock *>28buildStmtToBasicBlockMap(const CFG &Cfg) {29llvm::DenseMap<const Stmt *, const CFGBlock *> StmtToBlock;30for (const CFGBlock *Block : Cfg) {31if (Block == nullptr)32continue;3334for (const CFGElement &Element : *Block) {35auto Stmt = Element.getAs<CFGStmt>();36if (!Stmt)37continue;3839StmtToBlock[Stmt->getStmt()] = Block;40}41}42// Some terminator conditions don't appear as a `CFGElement` anywhere else -43// for example, this is true if the terminator condition is a `&&` or `||`44// operator.45// We associate these conditions with the block the terminator appears in,46// but only if the condition has not already appeared as a regular47// `CFGElement`. (The `insert()` below does nothing if the key already exists48// in the map.)49for (const CFGBlock *Block : Cfg) {50if (Block != nullptr)51if (const Stmt *TerminatorCond = Block->getTerminatorCondition())52StmtToBlock.insert({TerminatorCond, Block});53}54// Terminator statements typically don't appear as a `CFGElement` anywhere55// else, so we want to associate them with the block that they terminate.56// However, there are some important special cases:57// - The conditional operator is a type of terminator, but it also appears58// as a regular `CFGElement`, and we want to associate it with the block59// in which it appears as a `CFGElement`.60// - The `&&` and `||` operators are types of terminators, but like the61// conditional operator, they can appear as a regular `CFGElement` or62// as a terminator condition (see above).63// We process terminators last to make sure that we only associate them with64// the block they terminate if they haven't previously occurred as a regular65// `CFGElement` or as a terminator condition.66for (const CFGBlock *Block : Cfg) {67if (Block != nullptr)68if (const Stmt *TerminatorStmt = Block->getTerminatorStmt())69StmtToBlock.insert({TerminatorStmt, Block});70}71return StmtToBlock;72}7374static llvm::BitVector findReachableBlocks(const CFG &Cfg) {75llvm::BitVector BlockReachable(Cfg.getNumBlockIDs(), false);7677llvm::SmallVector<const CFGBlock *> BlocksToVisit;78BlocksToVisit.push_back(&Cfg.getEntry());79while (!BlocksToVisit.empty()) {80const CFGBlock *Block = BlocksToVisit.back();81BlocksToVisit.pop_back();8283if (BlockReachable[Block->getBlockID()])84continue;8586BlockReachable[Block->getBlockID()] = true;8788for (const CFGBlock *Succ : Block->succs())89if (Succ)90BlocksToVisit.push_back(Succ);91}9293return BlockReachable;94}9596static llvm::DenseSet<const CFGBlock *>97buildContainsExprConsumedInDifferentBlock(98const CFG &Cfg,99const llvm::DenseMap<const Stmt *, const CFGBlock *> &StmtToBlock) {100llvm::DenseSet<const CFGBlock *> Result;101102auto CheckChildExprs = [&Result, &StmtToBlock](const Stmt *S,103const CFGBlock *Block) {104for (const Stmt *Child : S->children()) {105if (!isa_and_nonnull<Expr>(Child))106continue;107const CFGBlock *ChildBlock = StmtToBlock.lookup(Child);108if (ChildBlock != Block)109Result.insert(ChildBlock);110}111};112113for (const CFGBlock *Block : Cfg) {114if (Block == nullptr)115continue;116117for (const CFGElement &Element : *Block)118if (auto S = Element.getAs<CFGStmt>())119CheckChildExprs(S->getStmt(), Block);120121if (const Stmt *TerminatorCond = Block->getTerminatorCondition())122CheckChildExprs(TerminatorCond, Block);123}124125return Result;126}127128llvm::Expected<AdornedCFG> AdornedCFG::build(const FunctionDecl &Func) {129if (!Func.doesThisDeclarationHaveABody())130return llvm::createStringError(131std::make_error_code(std::errc::invalid_argument),132"Cannot analyze function without a body");133134return build(Func, *Func.getBody(), Func.getASTContext());135}136137llvm::Expected<AdornedCFG> AdornedCFG::build(const Decl &D, Stmt &S,138ASTContext &C) {139if (D.isTemplated())140return llvm::createStringError(141std::make_error_code(std::errc::invalid_argument),142"Cannot analyze templated declarations");143144// The shape of certain elements of the AST can vary depending on the145// language. We currently only support C++.146if (!C.getLangOpts().CPlusPlus || C.getLangOpts().ObjC)147return llvm::createStringError(148std::make_error_code(std::errc::invalid_argument),149"Can only analyze C++");150151CFG::BuildOptions Options;152Options.PruneTriviallyFalseEdges = true;153Options.AddImplicitDtors = true;154Options.AddTemporaryDtors = true;155Options.AddInitializers = true;156Options.AddCXXDefaultInitExprInCtors = true;157Options.AddLifetime = true;158159// Ensure that all sub-expressions in basic blocks are evaluated.160Options.setAllAlwaysAdd();161162auto Cfg = CFG::buildCFG(&D, &S, &C, Options);163if (Cfg == nullptr)164return llvm::createStringError(165std::make_error_code(std::errc::invalid_argument),166"CFG::buildCFG failed");167168llvm::DenseMap<const Stmt *, const CFGBlock *> StmtToBlock =169buildStmtToBasicBlockMap(*Cfg);170171llvm::BitVector BlockReachable = findReachableBlocks(*Cfg);172173llvm::DenseSet<const CFGBlock *> ContainsExprConsumedInDifferentBlock =174buildContainsExprConsumedInDifferentBlock(*Cfg, StmtToBlock);175176return AdornedCFG(D, std::move(Cfg), std::move(StmtToBlock),177std::move(BlockReachable),178std::move(ContainsExprConsumedInDifferentBlock));179}180181} // namespace dataflow182} // namespace clang183184185