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 *> |
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(KV: {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(KV: {TerminatorStmt, Block}); |
71 | } |
72 | return StmtToBlock; |
73 | } |
74 | |
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(Elt: &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(Elt: Succ); |
92 | } |
93 | |
94 | return BlockReachable; |
95 | } |
96 | |
97 | static llvm::DenseSet<const CFGBlock *> |
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>(Val: Child)) |
107 | continue; |
108 | const CFGBlock *ChildBlock = StmtToBlock.lookup(Val: Child); |
109 | if (ChildBlock != Block) |
110 | Result.insert(V: 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 | |
129 | llvm::Expected<AdornedCFG> AdornedCFG::build(const FunctionDecl &Func) { |
130 | if (!Func.doesThisDeclarationHaveABody()) |
131 | return llvm::createStringError( |
132 | EC: std::make_error_code(e: std::errc::invalid_argument), |
133 | Msg: "Cannot analyze function without a body" ); |
134 | |
135 | return build(Func, *Func.getBody(), Func.getASTContext()); |
136 | } |
137 | |
138 | llvm::Expected<AdornedCFG> AdornedCFG::build(const Decl &D, Stmt &S, |
139 | ASTContext &C) { |
140 | if (D.isTemplated()) |
141 | return llvm::createStringError( |
142 | EC: std::make_error_code(e: std::errc::invalid_argument), |
143 | Msg: "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 | EC: std::make_error_code(e: std::errc::invalid_argument), |
150 | Msg: "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: &D, AST: &S, C: &C, BO: Options); |
164 | if (Cfg == nullptr) |
165 | return llvm::createStringError( |
166 | EC: std::make_error_code(e: std::errc::invalid_argument), |
167 | Msg: "CFG::buildCFG failed" ); |
168 | |
169 | llvm::DenseMap<const Stmt *, const CFGBlock *> StmtToBlock = |
170 | buildStmtToBasicBlockMap(Cfg: *Cfg); |
171 | |
172 | llvm::BitVector BlockReachable = findReachableBlocks(Cfg: *Cfg); |
173 | |
174 | llvm::DenseSet<const CFGBlock *> ContainsExprConsumedInDifferentBlock = |
175 | buildContainsExprConsumedInDifferentBlock(Cfg: *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 | |