xref: /openbsd-src/gnu/llvm/clang/lib/CodeGen/VarBypassDetector.cpp (revision 12c855180aad702bbcca06e0398d774beeafb155)
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