| //===--- VarBypassDetector.h - Bypass jumps detector --------------*- C++ -*-=// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "VarBypassDetector.h" |
| |
| #include "clang/AST/Decl.h" |
| #include "clang/AST/Expr.h" |
| #include "clang/AST/Stmt.h" |
| |
| using namespace clang; |
| using namespace CodeGen; |
| |
| /// Clear the object and pre-process for the given statement, usually function |
| /// body statement. |
| void VarBypassDetector::Init(const Stmt *Body) { |
| FromScopes.clear(); |
| ToScopes.clear(); |
| Bypasses.clear(); |
| Scopes = {{~0U, nullptr}}; |
| unsigned ParentScope = 0; |
| AlwaysBypassed = !BuildScopeInformation(Body, ParentScope); |
| if (!AlwaysBypassed) |
| Detect(); |
| } |
| |
| /// Build scope information for a declaration that is part of a DeclStmt. |
| /// Returns false if we failed to build scope information and can't tell for |
| /// which vars are being bypassed. |
| bool VarBypassDetector::BuildScopeInformation(const Decl *D, |
| unsigned &ParentScope) { |
| const VarDecl *VD = dyn_cast<VarDecl>(D); |
| if (VD && VD->hasLocalStorage()) { |
| Scopes.push_back({ParentScope, VD}); |
| ParentScope = Scopes.size() - 1; |
| } |
| |
| if (const VarDecl *VD = dyn_cast<VarDecl>(D)) |
| if (const Expr *Init = VD->getInit()) |
| return BuildScopeInformation(Init, ParentScope); |
| |
| return true; |
| } |
| |
| /// Walk through the statements, adding any labels or gotos to |
| /// LabelAndGotoScopes and recursively walking the AST as needed. |
| /// Returns false if we failed to build scope information and can't tell for |
| /// which vars are being bypassed. |
| bool VarBypassDetector::BuildScopeInformation(const Stmt *S, |
| unsigned &origParentScope) { |
| // If this is a statement, rather than an expression, scopes within it don't |
| // propagate out into the enclosing scope. Otherwise we have to worry about |
| // block literals, which have the lifetime of their enclosing statement. |
| unsigned independentParentScope = origParentScope; |
| unsigned &ParentScope = |
| ((isa<Expr>(S) && !isa<StmtExpr>(S)) ? origParentScope |
| : independentParentScope); |
| |
| unsigned StmtsToSkip = 0u; |
| |
| switch (S->getStmtClass()) { |
| case Stmt::IndirectGotoStmtClass: |
| return false; |
| |
| case Stmt::SwitchStmtClass: |
| if (const Stmt *Init = cast<SwitchStmt>(S)->getInit()) { |
| if (!BuildScopeInformation(Init, ParentScope)) |
| return false; |
| ++StmtsToSkip; |
| } |
| if (const VarDecl *Var = cast<SwitchStmt>(S)->getConditionVariable()) { |
| if (!BuildScopeInformation(Var, ParentScope)) |
| return false; |
| ++StmtsToSkip; |
| } |
| LLVM_FALLTHROUGH; |
| |
| case Stmt::GotoStmtClass: |
| FromScopes.push_back({S, ParentScope}); |
| break; |
| |
| case Stmt::DeclStmtClass: { |
| const DeclStmt *DS = cast<DeclStmt>(S); |
| for (auto *I : DS->decls()) |
| if (!BuildScopeInformation(I, origParentScope)) |
| return false; |
| return true; |
| } |
| |
| case Stmt::CaseStmtClass: |
| case Stmt::DefaultStmtClass: |
| case Stmt::LabelStmtClass: |
| llvm_unreachable("the loop below handles labels and cases"); |
| break; |
| |
| default: |
| break; |
| } |
| |
| for (const Stmt *SubStmt : S->children()) { |
| if (!SubStmt) |
| continue; |
| if (StmtsToSkip) { |
| --StmtsToSkip; |
| continue; |
| } |
| |
| // Cases, labels, and defaults aren't "scope parents". It's also |
| // important to handle these iteratively instead of recursively in |
| // order to avoid blowing out the stack. |
| while (true) { |
| const Stmt *Next; |
| if (const SwitchCase *SC = dyn_cast<SwitchCase>(SubStmt)) |
| Next = SC->getSubStmt(); |
| else if (const LabelStmt *LS = dyn_cast<LabelStmt>(SubStmt)) |
| Next = LS->getSubStmt(); |
| else |
| break; |
| |
| ToScopes[SubStmt] = ParentScope; |
| SubStmt = Next; |
| } |
| |
| // Recursively walk the AST. |
| if (!BuildScopeInformation(SubStmt, ParentScope)) |
| return false; |
| } |
| return true; |
| } |
| |
| /// Checks each jump and stores each variable declaration they bypass. |
| void VarBypassDetector::Detect() { |
| for (const auto &S : FromScopes) { |
| const Stmt *St = S.first; |
| unsigned from = S.second; |
| if (const GotoStmt *GS = dyn_cast<GotoStmt>(St)) { |
| if (const LabelStmt *LS = GS->getLabel()->getStmt()) |
| Detect(from, ToScopes[LS]); |
| } else if (const SwitchStmt *SS = dyn_cast<SwitchStmt>(St)) { |
| for (const SwitchCase *SC = SS->getSwitchCaseList(); SC; |
| SC = SC->getNextSwitchCase()) { |
| Detect(from, ToScopes[SC]); |
| } |
| } else { |
| llvm_unreachable("goto or switch was expected"); |
| } |
| } |
| } |
| |
| /// Checks the jump and stores each variable declaration it bypasses. |
| void VarBypassDetector::Detect(unsigned From, unsigned To) { |
| while (From != To) { |
| if (From < To) { |
| assert(Scopes[To].first < To); |
| const auto &ScopeTo = Scopes[To]; |
| To = ScopeTo.first; |
| Bypasses.insert(ScopeTo.second); |
| } else { |
| assert(Scopes[From].first < From); |
| From = Scopes[From].first; |
| } |
| } |
| } |