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