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