2016-10-26 13:42:30 +08:00
|
|
|
//===--- VarBypassDetector.h - Bypass jumps detector --------------*- C++ -*-=//
|
|
|
|
//
|
2019-01-19 16:50:56 +08:00
|
|
|
// 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
|
2016-10-26 13:42:30 +08:00
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
#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;
|
|
|
|
}
|
Fix clang -Wimplicit-fallthrough warnings across llvm, NFC
This patch should not introduce any behavior changes. It consists of
mostly one of two changes:
1. Replacing fall through comments with the LLVM_FALLTHROUGH macro
2. Inserting 'break' before falling through into a case block consisting
of only 'break'.
We were already using this warning with GCC, but its warning behaves
slightly differently. In this patch, the following differences are
relevant:
1. GCC recognizes comments that say "fall through" as annotations, clang
doesn't
2. GCC doesn't warn on "case N: foo(); default: break;", clang does
3. GCC doesn't warn when the case contains a switch, but falls through
the outer case.
I will enable the warning separately in a follow-up patch so that it can
be cleanly reverted if necessary.
Reviewers: alexfh, rsmith, lattner, rtrieu, EricWF, bollu
Differential Revision: https://reviews.llvm.org/D53950
llvm-svn: 345882
2018-11-02 03:54:45 +08:00
|
|
|
LLVM_FALLTHROUGH;
|
2016-10-26 13:42:30 +08:00
|
|
|
|
|
|
|
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:
|
2018-04-06 23:14:32 +08:00
|
|
|
llvm_unreachable("the loop below handles labels and cases");
|
2016-10-26 13:42:30 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|