1*9a887f65SVitaly Buka //===--- VarBypassDetector.cpp - Bypass jumps detector ------------*- C++ -*-=//
264c80b4eSVitaly Buka //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
664c80b4eSVitaly Buka //
764c80b4eSVitaly Buka //===----------------------------------------------------------------------===//
864c80b4eSVitaly Buka
964c80b4eSVitaly Buka #include "VarBypassDetector.h"
1064c80b4eSVitaly Buka
1164c80b4eSVitaly Buka #include "clang/AST/Decl.h"
1264c80b4eSVitaly Buka #include "clang/AST/Expr.h"
1364c80b4eSVitaly Buka #include "clang/AST/Stmt.h"
1464c80b4eSVitaly Buka
1564c80b4eSVitaly Buka using namespace clang;
1664c80b4eSVitaly Buka using namespace CodeGen;
1764c80b4eSVitaly Buka
1864c80b4eSVitaly Buka /// Clear the object and pre-process for the given statement, usually function
1964c80b4eSVitaly Buka /// body statement.
Init(const Stmt * Body)2064c80b4eSVitaly Buka void VarBypassDetector::Init(const Stmt *Body) {
2164c80b4eSVitaly Buka FromScopes.clear();
2264c80b4eSVitaly Buka ToScopes.clear();
2364c80b4eSVitaly Buka Bypasses.clear();
2464c80b4eSVitaly Buka Scopes = {{~0U, nullptr}};
2564c80b4eSVitaly Buka unsigned ParentScope = 0;
2664c80b4eSVitaly Buka AlwaysBypassed = !BuildScopeInformation(Body, ParentScope);
2764c80b4eSVitaly Buka if (!AlwaysBypassed)
2864c80b4eSVitaly Buka Detect();
2964c80b4eSVitaly Buka }
3064c80b4eSVitaly Buka
3164c80b4eSVitaly Buka /// Build scope information for a declaration that is part of a DeclStmt.
3264c80b4eSVitaly Buka /// Returns false if we failed to build scope information and can't tell for
3364c80b4eSVitaly Buka /// which vars are being bypassed.
BuildScopeInformation(const Decl * D,unsigned & ParentScope)3464c80b4eSVitaly Buka bool VarBypassDetector::BuildScopeInformation(const Decl *D,
3564c80b4eSVitaly Buka unsigned &ParentScope) {
3664c80b4eSVitaly Buka const VarDecl *VD = dyn_cast<VarDecl>(D);
3764c80b4eSVitaly Buka if (VD && VD->hasLocalStorage()) {
3864c80b4eSVitaly Buka Scopes.push_back({ParentScope, VD});
3964c80b4eSVitaly Buka ParentScope = Scopes.size() - 1;
4064c80b4eSVitaly Buka }
4164c80b4eSVitaly Buka
4264c80b4eSVitaly Buka if (const VarDecl *VD = dyn_cast<VarDecl>(D))
4364c80b4eSVitaly Buka if (const Expr *Init = VD->getInit())
4464c80b4eSVitaly Buka return BuildScopeInformation(Init, ParentScope);
4564c80b4eSVitaly Buka
4664c80b4eSVitaly Buka return true;
4764c80b4eSVitaly Buka }
4864c80b4eSVitaly Buka
4964c80b4eSVitaly Buka /// Walk through the statements, adding any labels or gotos to
5064c80b4eSVitaly Buka /// LabelAndGotoScopes and recursively walking the AST as needed.
5164c80b4eSVitaly Buka /// Returns false if we failed to build scope information and can't tell for
5264c80b4eSVitaly Buka /// which vars are being bypassed.
BuildScopeInformation(const Stmt * S,unsigned & origParentScope)5364c80b4eSVitaly Buka bool VarBypassDetector::BuildScopeInformation(const Stmt *S,
5464c80b4eSVitaly Buka unsigned &origParentScope) {
5564c80b4eSVitaly Buka // If this is a statement, rather than an expression, scopes within it don't
5664c80b4eSVitaly Buka // propagate out into the enclosing scope. Otherwise we have to worry about
5764c80b4eSVitaly Buka // block literals, which have the lifetime of their enclosing statement.
5864c80b4eSVitaly Buka unsigned independentParentScope = origParentScope;
5964c80b4eSVitaly Buka unsigned &ParentScope =
6064c80b4eSVitaly Buka ((isa<Expr>(S) && !isa<StmtExpr>(S)) ? origParentScope
6164c80b4eSVitaly Buka : independentParentScope);
6264c80b4eSVitaly Buka
6364c80b4eSVitaly Buka unsigned StmtsToSkip = 0u;
6464c80b4eSVitaly Buka
6564c80b4eSVitaly Buka switch (S->getStmtClass()) {
6664c80b4eSVitaly Buka case Stmt::IndirectGotoStmtClass:
6764c80b4eSVitaly Buka return false;
6864c80b4eSVitaly Buka
6964c80b4eSVitaly Buka case Stmt::SwitchStmtClass:
7064c80b4eSVitaly Buka if (const Stmt *Init = cast<SwitchStmt>(S)->getInit()) {
7164c80b4eSVitaly Buka if (!BuildScopeInformation(Init, ParentScope))
7264c80b4eSVitaly Buka return false;
7364c80b4eSVitaly Buka ++StmtsToSkip;
7464c80b4eSVitaly Buka }
7564c80b4eSVitaly Buka if (const VarDecl *Var = cast<SwitchStmt>(S)->getConditionVariable()) {
7664c80b4eSVitaly Buka if (!BuildScopeInformation(Var, ParentScope))
7764c80b4eSVitaly Buka return false;
7864c80b4eSVitaly Buka ++StmtsToSkip;
7964c80b4eSVitaly Buka }
804dc0b1acSReid Kleckner LLVM_FALLTHROUGH;
8164c80b4eSVitaly Buka
8264c80b4eSVitaly Buka case Stmt::GotoStmtClass:
8364c80b4eSVitaly Buka FromScopes.push_back({S, ParentScope});
8464c80b4eSVitaly Buka break;
8564c80b4eSVitaly Buka
8664c80b4eSVitaly Buka case Stmt::DeclStmtClass: {
8764c80b4eSVitaly Buka const DeclStmt *DS = cast<DeclStmt>(S);
8864c80b4eSVitaly Buka for (auto *I : DS->decls())
8964c80b4eSVitaly Buka if (!BuildScopeInformation(I, origParentScope))
9064c80b4eSVitaly Buka return false;
9164c80b4eSVitaly Buka return true;
9264c80b4eSVitaly Buka }
9364c80b4eSVitaly Buka
9464c80b4eSVitaly Buka case Stmt::CaseStmtClass:
9564c80b4eSVitaly Buka case Stmt::DefaultStmtClass:
9664c80b4eSVitaly Buka case Stmt::LabelStmtClass:
972a8c18d9SAlexander Kornienko llvm_unreachable("the loop below handles labels and cases");
9864c80b4eSVitaly Buka break;
9964c80b4eSVitaly Buka
10064c80b4eSVitaly Buka default:
10164c80b4eSVitaly Buka break;
10264c80b4eSVitaly Buka }
10364c80b4eSVitaly Buka
10464c80b4eSVitaly Buka for (const Stmt *SubStmt : S->children()) {
10564c80b4eSVitaly Buka if (!SubStmt)
10664c80b4eSVitaly Buka continue;
10764c80b4eSVitaly Buka if (StmtsToSkip) {
10864c80b4eSVitaly Buka --StmtsToSkip;
10964c80b4eSVitaly Buka continue;
11064c80b4eSVitaly Buka }
11164c80b4eSVitaly Buka
11264c80b4eSVitaly Buka // Cases, labels, and defaults aren't "scope parents". It's also
11364c80b4eSVitaly Buka // important to handle these iteratively instead of recursively in
11464c80b4eSVitaly Buka // order to avoid blowing out the stack.
11564c80b4eSVitaly Buka while (true) {
11664c80b4eSVitaly Buka const Stmt *Next;
11764c80b4eSVitaly Buka if (const SwitchCase *SC = dyn_cast<SwitchCase>(SubStmt))
11864c80b4eSVitaly Buka Next = SC->getSubStmt();
11964c80b4eSVitaly Buka else if (const LabelStmt *LS = dyn_cast<LabelStmt>(SubStmt))
12064c80b4eSVitaly Buka Next = LS->getSubStmt();
12164c80b4eSVitaly Buka else
12264c80b4eSVitaly Buka break;
12364c80b4eSVitaly Buka
12464c80b4eSVitaly Buka ToScopes[SubStmt] = ParentScope;
12564c80b4eSVitaly Buka SubStmt = Next;
12664c80b4eSVitaly Buka }
12764c80b4eSVitaly Buka
12864c80b4eSVitaly Buka // Recursively walk the AST.
12964c80b4eSVitaly Buka if (!BuildScopeInformation(SubStmt, ParentScope))
13064c80b4eSVitaly Buka return false;
13164c80b4eSVitaly Buka }
13264c80b4eSVitaly Buka return true;
13364c80b4eSVitaly Buka }
13464c80b4eSVitaly Buka
13564c80b4eSVitaly Buka /// Checks each jump and stores each variable declaration they bypass.
Detect()13664c80b4eSVitaly Buka void VarBypassDetector::Detect() {
13764c80b4eSVitaly Buka for (const auto &S : FromScopes) {
13864c80b4eSVitaly Buka const Stmt *St = S.first;
13964c80b4eSVitaly Buka unsigned from = S.second;
14064c80b4eSVitaly Buka if (const GotoStmt *GS = dyn_cast<GotoStmt>(St)) {
14164c80b4eSVitaly Buka if (const LabelStmt *LS = GS->getLabel()->getStmt())
14264c80b4eSVitaly Buka Detect(from, ToScopes[LS]);
14364c80b4eSVitaly Buka } else if (const SwitchStmt *SS = dyn_cast<SwitchStmt>(St)) {
14464c80b4eSVitaly Buka for (const SwitchCase *SC = SS->getSwitchCaseList(); SC;
14564c80b4eSVitaly Buka SC = SC->getNextSwitchCase()) {
14664c80b4eSVitaly Buka Detect(from, ToScopes[SC]);
14764c80b4eSVitaly Buka }
14864c80b4eSVitaly Buka } else {
14964c80b4eSVitaly Buka llvm_unreachable("goto or switch was expected");
15064c80b4eSVitaly Buka }
15164c80b4eSVitaly Buka }
15264c80b4eSVitaly Buka }
15364c80b4eSVitaly Buka
15464c80b4eSVitaly Buka /// Checks the jump and stores each variable declaration it bypasses.
Detect(unsigned From,unsigned To)15564c80b4eSVitaly Buka void VarBypassDetector::Detect(unsigned From, unsigned To) {
15664c80b4eSVitaly Buka while (From != To) {
15764c80b4eSVitaly Buka if (From < To) {
15864c80b4eSVitaly Buka assert(Scopes[To].first < To);
15964c80b4eSVitaly Buka const auto &ScopeTo = Scopes[To];
16064c80b4eSVitaly Buka To = ScopeTo.first;
16164c80b4eSVitaly Buka Bypasses.insert(ScopeTo.second);
16264c80b4eSVitaly Buka } else {
16364c80b4eSVitaly Buka assert(Scopes[From].first < From);
16464c80b4eSVitaly Buka From = Scopes[From].first;
16564c80b4eSVitaly Buka }
16664c80b4eSVitaly Buka }
16764c80b4eSVitaly Buka }
168