xref: /freebsd-src/contrib/llvm-project/clang/lib/Analysis/FlowSensitive/AdornedCFG.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
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