/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/CodeGen/VarBypassDetector.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //===--- VarBypassDetector.cpp - Bypass jumps detector ------------*- C++ -*-=// |
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 | | #include "VarBypassDetector.h" |
10 | | |
11 | | #include "clang/AST/Decl.h" |
12 | | #include "clang/AST/Expr.h" |
13 | | #include "clang/AST/Stmt.h" |
14 | | |
15 | | using namespace clang; |
16 | | using namespace CodeGen; |
17 | | |
18 | | /// Clear the object and pre-process for the given statement, usually function |
19 | | /// body statement. |
20 | 40.7k | void VarBypassDetector::Init(const Stmt *Body) { |
21 | 40.7k | FromScopes.clear(); |
22 | 40.7k | ToScopes.clear(); |
23 | 40.7k | Bypasses.clear(); |
24 | 40.7k | Scopes = {{~0U, nullptr}}; |
25 | 40.7k | unsigned ParentScope = 0; |
26 | 40.7k | AlwaysBypassed = !BuildScopeInformation(Body, ParentScope); |
27 | 40.7k | if (!AlwaysBypassed) |
28 | 40.7k | Detect(); |
29 | 40.7k | } |
30 | | |
31 | | /// Build scope information for a declaration that is part of a DeclStmt. |
32 | | /// Returns false if we failed to build scope information and can't tell for |
33 | | /// which vars are being bypassed. |
34 | | bool VarBypassDetector::BuildScopeInformation(const Decl *D, |
35 | 3.76k | unsigned &ParentScope) { |
36 | 3.76k | const VarDecl *VD = dyn_cast<VarDecl>(D); |
37 | 3.76k | if (VD && VD->hasLocalStorage()3.57k ) { |
38 | 3.40k | Scopes.push_back({ParentScope, VD}); |
39 | 3.40k | ParentScope = Scopes.size() - 1; |
40 | 3.40k | } |
41 | | |
42 | 3.76k | if (const VarDecl *VD = dyn_cast<VarDecl>(D)) |
43 | 3.57k | if (const Expr *Init = VD->getInit()) |
44 | 2.59k | return BuildScopeInformation(Init, ParentScope); |
45 | | |
46 | 1.16k | return true; |
47 | 3.76k | } |
48 | | |
49 | | /// Walk through the statements, adding any labels or gotos to |
50 | | /// LabelAndGotoScopes and recursively walking the AST as needed. |
51 | | /// Returns false if we failed to build scope information and can't tell for |
52 | | /// which vars are being bypassed. |
53 | | bool VarBypassDetector::BuildScopeInformation(const Stmt *S, |
54 | 442k | unsigned &origParentScope) { |
55 | | // If this is a statement, rather than an expression, scopes within it don't |
56 | | // propagate out into the enclosing scope. Otherwise we have to worry about |
57 | | // block literals, which have the lifetime of their enclosing statement. |
58 | 442k | unsigned independentParentScope = origParentScope; |
59 | 442k | unsigned &ParentScope = |
60 | 442k | ((isa<Expr>(S) && !isa<StmtExpr>(S)355k ) ? origParentScope355k |
61 | 442k | : independentParentScope87.6k ); |
62 | | |
63 | 442k | unsigned StmtsToSkip = 0u; |
64 | | |
65 | 442k | switch (S->getStmtClass()) { |
66 | 3 | case Stmt::IndirectGotoStmtClass: |
67 | 3 | return false; |
68 | | |
69 | 66 | case Stmt::SwitchStmtClass: |
70 | 66 | if (const Stmt *Init = cast<SwitchStmt>(S)->getInit()) { |
71 | 0 | if (!BuildScopeInformation(Init, ParentScope)) |
72 | 0 | return false; |
73 | 0 | ++StmtsToSkip; |
74 | 0 | } |
75 | 66 | if (const VarDecl *Var = cast<SwitchStmt>(S)->getConditionVariable()) { |
76 | 0 | if (!BuildScopeInformation(Var, ParentScope)) |
77 | 0 | return false; |
78 | 0 | ++StmtsToSkip; |
79 | 0 | } |
80 | 66 | LLVM_FALLTHROUGH; |
81 | | |
82 | 3.07k | case Stmt::GotoStmtClass: |
83 | 3.07k | FromScopes.push_back({S, ParentScope}); |
84 | 3.07k | break; |
85 | | |
86 | 3.65k | case Stmt::DeclStmtClass: { |
87 | 3.65k | const DeclStmt *DS = cast<DeclStmt>(S); |
88 | 3.65k | for (auto *I : DS->decls()) |
89 | 3.76k | if (!BuildScopeInformation(I, origParentScope)) |
90 | 0 | return false; |
91 | 3.65k | return true; |
92 | 3.65k | } |
93 | | |
94 | 0 | case Stmt::CaseStmtClass: |
95 | 0 | case Stmt::DefaultStmtClass: |
96 | 0 | case Stmt::LabelStmtClass: |
97 | 0 | llvm_unreachable("the loop below handles labels and cases"); |
98 | 0 | break; |
99 | | |
100 | 435k | default: |
101 | 435k | break; |
102 | 442k | } |
103 | | |
104 | 439k | for (const Stmt *SubStmt : S->children()) { |
105 | 399k | if (!SubStmt) |
106 | 619 | continue; |
107 | 399k | if (StmtsToSkip) { |
108 | 0 | --StmtsToSkip; |
109 | 0 | continue; |
110 | 0 | } |
111 | | |
112 | | // Cases, labels, and defaults aren't "scope parents". It's also |
113 | | // important to handle these iteratively instead of recursively in |
114 | | // order to avoid blowing out the stack. |
115 | 399k | while (399k true) { |
116 | 399k | const Stmt *Next; |
117 | 399k | if (const SwitchCase *SC = dyn_cast<SwitchCase>(SubStmt)) |
118 | 192 | Next = SC->getSubStmt(); |
119 | 399k | else if (const LabelStmt *LS = dyn_cast<LabelStmt>(SubStmt)) |
120 | 17 | Next = LS->getSubStmt(); |
121 | 399k | else |
122 | 399k | break; |
123 | | |
124 | 209 | ToScopes[SubStmt] = ParentScope; |
125 | 209 | SubStmt = Next; |
126 | 209 | } |
127 | | |
128 | | // Recursively walk the AST. |
129 | 399k | if (!BuildScopeInformation(SubStmt, ParentScope)) |
130 | 3 | return false; |
131 | 399k | } |
132 | 439k | return true; |
133 | 439k | } |
134 | | |
135 | | /// Checks each jump and stores each variable declaration they bypass. |
136 | 40.7k | void VarBypassDetector::Detect() { |
137 | 40.7k | for (const auto &S : FromScopes) { |
138 | 3.07k | const Stmt *St = S.first; |
139 | 3.07k | unsigned from = S.second; |
140 | 3.07k | if (const GotoStmt *GS = dyn_cast<GotoStmt>(St)) { |
141 | 3.01k | if (const LabelStmt *LS = GS->getLabel()->getStmt()) |
142 | 3.01k | Detect(from, ToScopes[LS]); |
143 | 3.01k | } else if (const SwitchStmt *66 SS66 = dyn_cast<SwitchStmt>(St)) { |
144 | 258 | for (const SwitchCase *SC = SS->getSwitchCaseList(); SC; |
145 | 192 | SC = SC->getNextSwitchCase()) { |
146 | 192 | Detect(from, ToScopes[SC]); |
147 | 192 | } |
148 | 66 | } else { |
149 | 0 | llvm_unreachable("goto or switch was expected"); |
150 | 0 | } |
151 | 3.07k | } |
152 | 40.7k | } |
153 | | |
154 | | /// Checks the jump and stores each variable declaration it bypasses. |
155 | 3.20k | void VarBypassDetector::Detect(unsigned From, unsigned To) { |
156 | 3.21k | while (From != To) { |
157 | 8 | if (From < To) { |
158 | 2 | assert(Scopes[To].first < To); |
159 | 0 | const auto &ScopeTo = Scopes[To]; |
160 | 2 | To = ScopeTo.first; |
161 | 2 | Bypasses.insert(ScopeTo.second); |
162 | 6 | } else { |
163 | 6 | assert(Scopes[From].first < From); |
164 | 0 | From = Scopes[From].first; |
165 | 6 | } |
166 | 8 | } |
167 | 3.20k | } |