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 | void VarBypassDetector::Init(const Stmt *Body) { |
21 | FromScopes.clear(); |
22 | ToScopes.clear(); |
23 | Bypasses.clear(); |
24 | Scopes = {{~0U, nullptr}}; |
25 | unsigned ParentScope = 0; |
26 | AlwaysBypassed = !BuildScopeInformation(S: Body, origParentScope&: ParentScope); |
27 | if (!AlwaysBypassed) |
28 | Detect(); |
29 | } |
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 | unsigned &ParentScope) { |
36 | const VarDecl *VD = dyn_cast<VarDecl>(Val: D); |
37 | if (VD && VD->hasLocalStorage()) { |
38 | Scopes.push_back(Elt: {ParentScope, VD}); |
39 | ParentScope = Scopes.size() - 1; |
40 | } |
41 | |
42 | if (const VarDecl *VD = dyn_cast<VarDecl>(Val: D)) |
43 | if (const Expr *Init = VD->getInit()) |
44 | return BuildScopeInformation(Init, ParentScope); |
45 | |
46 | return true; |
47 | } |
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 | 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 | unsigned independentParentScope = origParentScope; |
59 | unsigned &ParentScope = |
60 | ((isa<Expr>(Val: S) && !isa<StmtExpr>(Val: S)) ? origParentScope |
61 | : independentParentScope); |
62 | |
63 | unsigned StmtsToSkip = 0u; |
64 | |
65 | switch (S->getStmtClass()) { |
66 | case Stmt::IndirectGotoStmtClass: |
67 | return false; |
68 | |
69 | case Stmt::SwitchStmtClass: |
70 | if (const Stmt *Init = cast<SwitchStmt>(Val: S)->getInit()) { |
71 | if (!BuildScopeInformation(S: Init, origParentScope&: ParentScope)) |
72 | return false; |
73 | ++StmtsToSkip; |
74 | } |
75 | if (const VarDecl *Var = cast<SwitchStmt>(Val: S)->getConditionVariable()) { |
76 | if (!BuildScopeInformation(Var, ParentScope)) |
77 | return false; |
78 | ++StmtsToSkip; |
79 | } |
80 | [[fallthrough]]; |
81 | |
82 | case Stmt::GotoStmtClass: |
83 | FromScopes.push_back(Elt: {S, ParentScope}); |
84 | break; |
85 | |
86 | case Stmt::DeclStmtClass: { |
87 | const DeclStmt *DS = cast<DeclStmt>(Val: S); |
88 | for (auto *I : DS->decls()) |
89 | if (!BuildScopeInformation(D: I, ParentScope&: origParentScope)) |
90 | return false; |
91 | return true; |
92 | } |
93 | |
94 | case Stmt::CaseStmtClass: |
95 | case Stmt::DefaultStmtClass: |
96 | case Stmt::LabelStmtClass: |
97 | llvm_unreachable("the loop below handles labels and cases" ); |
98 | break; |
99 | |
100 | default: |
101 | break; |
102 | } |
103 | |
104 | for (const Stmt *SubStmt : S->children()) { |
105 | if (!SubStmt) |
106 | continue; |
107 | if (StmtsToSkip) { |
108 | --StmtsToSkip; |
109 | continue; |
110 | } |
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 | while (true) { |
116 | const Stmt *Next; |
117 | if (const SwitchCase *SC = dyn_cast<SwitchCase>(Val: SubStmt)) |
118 | Next = SC->getSubStmt(); |
119 | else if (const LabelStmt *LS = dyn_cast<LabelStmt>(Val: SubStmt)) |
120 | Next = LS->getSubStmt(); |
121 | else |
122 | break; |
123 | |
124 | ToScopes[SubStmt] = ParentScope; |
125 | SubStmt = Next; |
126 | } |
127 | |
128 | // Recursively walk the AST. |
129 | if (!BuildScopeInformation(S: SubStmt, origParentScope&: ParentScope)) |
130 | return false; |
131 | } |
132 | return true; |
133 | } |
134 | |
135 | /// Checks each jump and stores each variable declaration they bypass. |
136 | void VarBypassDetector::Detect() { |
137 | for (const auto &S : FromScopes) { |
138 | const Stmt *St = S.first; |
139 | unsigned from = S.second; |
140 | if (const GotoStmt *GS = dyn_cast<GotoStmt>(Val: St)) { |
141 | if (const LabelStmt *LS = GS->getLabel()->getStmt()) |
142 | Detect(From: from, To: ToScopes[LS]); |
143 | } else if (const SwitchStmt *SS = dyn_cast<SwitchStmt>(Val: St)) { |
144 | for (const SwitchCase *SC = SS->getSwitchCaseList(); SC; |
145 | SC = SC->getNextSwitchCase()) { |
146 | Detect(From: from, To: ToScopes[SC]); |
147 | } |
148 | } else { |
149 | llvm_unreachable("goto or switch was expected" ); |
150 | } |
151 | } |
152 | } |
153 | |
154 | /// Checks the jump and stores each variable declaration it bypasses. |
155 | void VarBypassDetector::Detect(unsigned From, unsigned To) { |
156 | while (From != To) { |
157 | if (From < To) { |
158 | assert(Scopes[To].first < To); |
159 | const auto &ScopeTo = Scopes[To]; |
160 | To = ScopeTo.first; |
161 | Bypasses.insert(V: ScopeTo.second); |
162 | } else { |
163 | assert(Scopes[From].first < From); |
164 | From = Scopes[From].first; |
165 | } |
166 | } |
167 | } |
168 | |