forked from OSchip/llvm-project
265 lines
11 KiB
C++
265 lines
11 KiB
C++
//===--- IdDependentBackwardBranchCheck.cpp - clang-tidy ------------------===//
|
|
//
|
|
// 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 "IdDependentBackwardBranchCheck.h"
|
|
#include "clang/AST/ASTContext.h"
|
|
#include "clang/ASTMatchers/ASTMatchFinder.h"
|
|
|
|
using namespace clang::ast_matchers;
|
|
|
|
namespace clang {
|
|
namespace tidy {
|
|
namespace altera {
|
|
|
|
void IdDependentBackwardBranchCheck::registerMatchers(MatchFinder *Finder) {
|
|
// Prototype to identify all variables which hold a thread-variant ID.
|
|
// First Matcher just finds all the direct assignments of either ID call.
|
|
const auto ThreadID = expr(hasDescendant(callExpr(callee(functionDecl(
|
|
anyOf(hasName("get_global_id"), hasName("get_local_id")))))));
|
|
|
|
const auto RefVarOrField = forEachDescendant(
|
|
stmt(anyOf(declRefExpr(to(varDecl())).bind("assign_ref_var"),
|
|
memberExpr(member(fieldDecl())).bind("assign_ref_field"))));
|
|
|
|
Finder->addMatcher(
|
|
compoundStmt(
|
|
// Bind on actual get_local/global_id calls.
|
|
forEachDescendant(
|
|
stmt(
|
|
anyOf(declStmt(hasDescendant(varDecl(hasInitializer(ThreadID))
|
|
.bind("tid_dep_var"))),
|
|
binaryOperator(allOf(
|
|
isAssignmentOperator(), hasRHS(ThreadID),
|
|
hasLHS(anyOf(
|
|
declRefExpr(to(varDecl().bind("tid_dep_var"))),
|
|
memberExpr(member(
|
|
fieldDecl().bind("tid_dep_field")))))))))
|
|
.bind("straight_assignment"))),
|
|
this);
|
|
|
|
// Bind all VarDecls that include an initializer with a variable DeclRefExpr
|
|
// (in case it is ID-dependent).
|
|
Finder->addMatcher(
|
|
stmt(forEachDescendant(
|
|
varDecl(hasInitializer(RefVarOrField)).bind("pot_tid_var"))),
|
|
this);
|
|
|
|
// Bind all VarDecls that are assigned a value with a variable DeclRefExpr (in
|
|
// case it is ID-dependent).
|
|
Finder->addMatcher(
|
|
stmt(forEachDescendant(binaryOperator(
|
|
allOf(isAssignmentOperator(), hasRHS(RefVarOrField),
|
|
hasLHS(anyOf(
|
|
declRefExpr(to(varDecl().bind("pot_tid_var"))),
|
|
memberExpr(member(fieldDecl().bind("pot_tid_field"))))))))),
|
|
this);
|
|
|
|
// Second Matcher looks for branch statements inside of loops and bind on the
|
|
// condition expression IF it either calls an ID function or has a variable
|
|
// DeclRefExpr. DeclRefExprs are checked later to confirm whether the variable
|
|
// is ID-dependent.
|
|
const auto CondExpr =
|
|
expr(anyOf(hasDescendant(callExpr(callee(functionDecl(
|
|
anyOf(hasName("get_global_id"),
|
|
hasName("get_local_id")))))
|
|
.bind("id_call")),
|
|
hasDescendant(stmt(anyOf(declRefExpr(to(varDecl())),
|
|
memberExpr(member(fieldDecl())))))))
|
|
.bind("cond_expr");
|
|
Finder->addMatcher(stmt(anyOf(forStmt(hasCondition(CondExpr)),
|
|
doStmt(hasCondition(CondExpr)),
|
|
whileStmt(hasCondition(CondExpr))))
|
|
.bind("backward_branch"),
|
|
this);
|
|
}
|
|
|
|
IdDependentBackwardBranchCheck::IdDependencyRecord *
|
|
IdDependentBackwardBranchCheck::hasIdDepVar(const Expr *Expression) {
|
|
if (const auto *Declaration = dyn_cast<DeclRefExpr>(Expression)) {
|
|
// It is a DeclRefExpr, so check if it's an ID-dependent variable.
|
|
const auto *CheckVariable = dyn_cast<VarDecl>(Declaration->getDecl());
|
|
auto FoundVariable = IdDepVarsMap.find(CheckVariable);
|
|
if (FoundVariable == IdDepVarsMap.end())
|
|
return nullptr;
|
|
return &(FoundVariable->second);
|
|
}
|
|
for (const auto *Child : Expression->children())
|
|
if (const auto *ChildExpression = dyn_cast<Expr>(Child))
|
|
if (IdDependencyRecord *Result = hasIdDepVar(ChildExpression))
|
|
return Result;
|
|
return nullptr;
|
|
}
|
|
|
|
IdDependentBackwardBranchCheck::IdDependencyRecord *
|
|
IdDependentBackwardBranchCheck::hasIdDepField(const Expr *Expression) {
|
|
if (const auto *MemberExpression = dyn_cast<MemberExpr>(Expression)) {
|
|
const auto *CheckField =
|
|
dyn_cast<FieldDecl>(MemberExpression->getMemberDecl());
|
|
auto FoundField = IdDepFieldsMap.find(CheckField);
|
|
if (FoundField == IdDepFieldsMap.end())
|
|
return nullptr;
|
|
return &(FoundField->second);
|
|
}
|
|
for (const auto *Child : Expression->children())
|
|
if (const auto *ChildExpression = dyn_cast<Expr>(Child))
|
|
if (IdDependencyRecord *Result = hasIdDepField(ChildExpression))
|
|
return Result;
|
|
return nullptr;
|
|
}
|
|
|
|
void IdDependentBackwardBranchCheck::saveIdDepVar(const Stmt *Statement,
|
|
const VarDecl *Variable) {
|
|
// Record that this variable is thread-dependent.
|
|
IdDepVarsMap[Variable] =
|
|
IdDependencyRecord(Variable, Variable->getBeginLoc(),
|
|
Twine("assignment of ID-dependent variable ") +
|
|
Variable->getNameAsString());
|
|
}
|
|
|
|
void IdDependentBackwardBranchCheck::saveIdDepField(const Stmt *Statement,
|
|
const FieldDecl *Field) {
|
|
// Record that this field is thread-dependent.
|
|
IdDepFieldsMap[Field] = IdDependencyRecord(
|
|
Field, Statement->getBeginLoc(),
|
|
Twine("assignment of ID-dependent field ") + Field->getNameAsString());
|
|
}
|
|
|
|
void IdDependentBackwardBranchCheck::saveIdDepVarFromReference(
|
|
const DeclRefExpr *RefExpr, const MemberExpr *MemExpr,
|
|
const VarDecl *PotentialVar) {
|
|
// If the variable is already in IdDepVarsMap, ignore it.
|
|
if (IdDepVarsMap.find(PotentialVar) != IdDepVarsMap.end())
|
|
return;
|
|
std::string Message;
|
|
llvm::raw_string_ostream StringStream(Message);
|
|
StringStream << "inferred assignment of ID-dependent value from "
|
|
"ID-dependent ";
|
|
if (RefExpr) {
|
|
const auto *RefVar = dyn_cast<VarDecl>(RefExpr->getDecl());
|
|
// If variable isn't ID-dependent, but RefVar is.
|
|
if (IdDepVarsMap.find(RefVar) != IdDepVarsMap.end())
|
|
StringStream << "variable " << RefVar->getNameAsString();
|
|
}
|
|
if (MemExpr) {
|
|
const auto *RefField = dyn_cast<FieldDecl>(MemExpr->getMemberDecl());
|
|
// If variable isn't ID-dependent, but RefField is.
|
|
if (IdDepFieldsMap.find(RefField) != IdDepFieldsMap.end())
|
|
StringStream << "member " << RefField->getNameAsString();
|
|
}
|
|
IdDepVarsMap[PotentialVar] =
|
|
IdDependencyRecord(PotentialVar, PotentialVar->getBeginLoc(), Message);
|
|
}
|
|
|
|
void IdDependentBackwardBranchCheck::saveIdDepFieldFromReference(
|
|
const DeclRefExpr *RefExpr, const MemberExpr *MemExpr,
|
|
const FieldDecl *PotentialField) {
|
|
// If the field is already in IdDepFieldsMap, ignore it.
|
|
if (IdDepFieldsMap.find(PotentialField) != IdDepFieldsMap.end())
|
|
return;
|
|
std::string Message;
|
|
llvm::raw_string_ostream StringStream(Message);
|
|
StringStream << "inferred assignment of ID-dependent member from "
|
|
"ID-dependent ";
|
|
if (RefExpr) {
|
|
const auto *RefVar = dyn_cast<VarDecl>(RefExpr->getDecl());
|
|
// If field isn't ID-dependent, but RefVar is.
|
|
if (IdDepVarsMap.find(RefVar) != IdDepVarsMap.end())
|
|
StringStream << "variable " << RefVar->getNameAsString();
|
|
}
|
|
if (MemExpr) {
|
|
const auto *RefField = dyn_cast<FieldDecl>(MemExpr->getMemberDecl());
|
|
if (IdDepFieldsMap.find(RefField) != IdDepFieldsMap.end())
|
|
StringStream << "member " << RefField->getNameAsString();
|
|
}
|
|
IdDepFieldsMap[PotentialField] = IdDependencyRecord(
|
|
PotentialField, PotentialField->getBeginLoc(), Message);
|
|
}
|
|
|
|
IdDependentBackwardBranchCheck::LoopType
|
|
IdDependentBackwardBranchCheck::getLoopType(const Stmt *Loop) {
|
|
switch (Loop->getStmtClass()) {
|
|
case Stmt::DoStmtClass:
|
|
return DoLoop;
|
|
case Stmt::WhileStmtClass:
|
|
return WhileLoop;
|
|
case Stmt::ForStmtClass:
|
|
return ForLoop;
|
|
default:
|
|
return UnknownLoop;
|
|
}
|
|
}
|
|
|
|
void IdDependentBackwardBranchCheck::check(
|
|
const MatchFinder::MatchResult &Result) {
|
|
// The first half of the callback only deals with identifying and storing
|
|
// ID-dependency information into the IdDepVars and IdDepFields maps.
|
|
const auto *Variable = Result.Nodes.getNodeAs<VarDecl>("tid_dep_var");
|
|
const auto *Field = Result.Nodes.getNodeAs<FieldDecl>("tid_dep_field");
|
|
const auto *Statement = Result.Nodes.getNodeAs<Stmt>("straight_assignment");
|
|
const auto *RefExpr = Result.Nodes.getNodeAs<DeclRefExpr>("assign_ref_var");
|
|
const auto *MemExpr = Result.Nodes.getNodeAs<MemberExpr>("assign_ref_field");
|
|
const auto *PotentialVar = Result.Nodes.getNodeAs<VarDecl>("pot_tid_var");
|
|
const auto *PotentialField =
|
|
Result.Nodes.getNodeAs<FieldDecl>("pot_tid_field");
|
|
|
|
// Save variables and fields assigned directly through ID function calls.
|
|
if (Statement && (Variable || Field)) {
|
|
if (Variable)
|
|
saveIdDepVar(Statement, Variable);
|
|
else if (Field)
|
|
saveIdDepField(Statement, Field);
|
|
}
|
|
|
|
// Save variables assigned to values of Id-dependent variables and fields.
|
|
if ((RefExpr || MemExpr) && PotentialVar)
|
|
saveIdDepVarFromReference(RefExpr, MemExpr, PotentialVar);
|
|
|
|
// Save fields assigned to values of ID-dependent variables and fields.
|
|
if ((RefExpr || MemExpr) && PotentialField)
|
|
saveIdDepFieldFromReference(RefExpr, MemExpr, PotentialField);
|
|
|
|
// The second part of the callback deals with checking if a branch inside a
|
|
// loop is thread dependent.
|
|
const auto *CondExpr = Result.Nodes.getNodeAs<Expr>("cond_expr");
|
|
const auto *IDCall = Result.Nodes.getNodeAs<CallExpr>("id_call");
|
|
const auto *Loop = Result.Nodes.getNodeAs<Stmt>("backward_branch");
|
|
if (!Loop)
|
|
return;
|
|
LoopType Type = getLoopType(Loop);
|
|
if (CondExpr) {
|
|
if (IDCall) { // Conditional expression calls an ID function directly.
|
|
diag(CondExpr->getBeginLoc(),
|
|
"backward branch (%select{do|while|for}0 loop) is ID-dependent due "
|
|
"to ID function call and may cause performance degradation")
|
|
<< Type;
|
|
return;
|
|
}
|
|
// Conditional expression has DeclRefExpr(s), check ID-dependency.
|
|
IdDependencyRecord *IdDepVar = hasIdDepVar(CondExpr);
|
|
IdDependencyRecord *IdDepField = hasIdDepField(CondExpr);
|
|
if (IdDepVar) {
|
|
// Change one of these to a Note
|
|
diag(IdDepVar->Location, IdDepVar->Message, DiagnosticIDs::Note);
|
|
diag(CondExpr->getBeginLoc(),
|
|
"backward branch (%select{do|while|for}0 loop) is ID-dependent due "
|
|
"to variable reference to %1 and may cause performance degradation")
|
|
<< Type << IdDepVar->VariableDeclaration;
|
|
} else if (IdDepField) {
|
|
diag(IdDepField->Location, IdDepField->Message, DiagnosticIDs::Note);
|
|
diag(CondExpr->getBeginLoc(),
|
|
"backward branch (%select{do|while|for}0 loop) is ID-dependent due "
|
|
"to member reference to %1 and may cause performance degradation")
|
|
<< Type << IdDepField->FieldDeclaration;
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace altera
|
|
} // namespace tidy
|
|
} // namespace clang
|