llvm-project/clang/lib/CodeGen/VarBypassDetector.cpp

168 lines
5.1 KiB
C++

//===--- 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;
}
}
}