xref: /llvm-project/clang/lib/Analysis/FlowSensitive/AdornedCFG.cpp (revision 0362a29905ab8d68a8eb48741840a514b66552f8)
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