10#include "clang/AST/ASTContext.h"
11#include "clang/ASTMatchers/ASTMatchFinder.h"
20 const auto ThreadID = expr(hasDescendant(callExpr(callee(functionDecl(
21 anyOf(hasName(
"get_global_id"), hasName(
"get_local_id")))))));
23 const auto RefVarOrField = forEachDescendant(
24 stmt(anyOf(declRefExpr(to(varDecl())).bind(
"assign_ref_var"),
25 memberExpr(member(fieldDecl())).bind(
"assign_ref_field"))));
32 anyOf(declStmt(hasDescendant(varDecl(hasInitializer(ThreadID))
33 .bind(
"tid_dep_var"))),
35 isAssignmentOperator(), hasRHS(ThreadID),
37 declRefExpr(to(varDecl().bind(
"tid_dep_var"))),
39 fieldDecl().bind(
"tid_dep_field"))))))))
40 .bind(
"straight_assignment"))),
46 stmt(forEachDescendant(
47 varDecl(hasInitializer(RefVarOrField)).bind(
"pot_tid_var"))),
53 stmt(forEachDescendant(binaryOperator(
54 allOf(isAssignmentOperator(), hasRHS(RefVarOrField),
56 declRefExpr(to(varDecl().bind(
"pot_tid_var"))),
57 memberExpr(member(fieldDecl().bind(
"pot_tid_field"))))))))),
65 expr(anyOf(hasDescendant(callExpr(callee(functionDecl(
66 anyOf(hasName(
"get_global_id"),
67 hasName(
"get_local_id")))))
69 hasDescendant(stmt(anyOf(declRefExpr(to(varDecl())),
70 memberExpr(member(fieldDecl())))))))
72 Finder->addMatcher(stmt(anyOf(forStmt(hasCondition(CondExpr)),
73 doStmt(hasCondition(CondExpr)),
74 whileStmt(hasCondition(CondExpr))))
75 .bind(
"backward_branch"),
79IdDependentBackwardBranchCheck::IdDependencyRecord *
80IdDependentBackwardBranchCheck::hasIdDepVar(
const Expr *Expression) {
84 if (
const auto *Declaration = dyn_cast<DeclRefExpr>(Expression)) {
86 const auto *CheckVariable =
87 dyn_cast_if_present<VarDecl>(Declaration->getDecl());
90 auto FoundVariable = IdDepVarsMap.find(CheckVariable);
91 if (FoundVariable == IdDepVarsMap.end())
93 return &(FoundVariable->second);
95 for (
const auto *Child : Expression->children())
96 if (
const auto *ChildExpression = dyn_cast_if_present<Expr>(Child))
97 if (IdDependencyRecord *Result = hasIdDepVar(ChildExpression))
102IdDependentBackwardBranchCheck::IdDependencyRecord *
103IdDependentBackwardBranchCheck::hasIdDepField(
const Expr *Expression) {
107 if (
const auto *MemberExpression = dyn_cast<MemberExpr>(Expression)) {
108 const auto *CheckField =
109 dyn_cast_if_present<FieldDecl>(MemberExpression->getMemberDecl());
112 auto FoundField = IdDepFieldsMap.find(CheckField);
113 if (FoundField == IdDepFieldsMap.end())
115 return &(FoundField->second);
117 for (
const auto *Child : Expression->children())
118 if (
const auto *ChildExpression = dyn_cast_if_present<Expr>(Child))
119 if (IdDependencyRecord *Result = hasIdDepField(ChildExpression))
124void IdDependentBackwardBranchCheck::saveIdDepVar(
const Stmt *Statement,
125 const VarDecl *Variable) {
128 IdDependencyRecord(Variable,
Variable->getBeginLoc(),
129 Twine(
"assignment of ID-dependent variable ") +
133void IdDependentBackwardBranchCheck::saveIdDepField(
const Stmt *Statement,
134 const FieldDecl *Field) {
136 IdDepFieldsMap[
Field] = IdDependencyRecord(
137 Field, Statement->getBeginLoc(),
138 Twine(
"assignment of ID-dependent field ") +
Field->getNameAsString());
141void IdDependentBackwardBranchCheck::saveIdDepVarFromReference(
142 const DeclRefExpr *RefExpr,
const MemberExpr *MemExpr,
143 const VarDecl *PotentialVar) {
145 if (IdDepVarsMap.find(PotentialVar) != IdDepVarsMap.end())
148 llvm::raw_string_ostream StringStream(Message);
149 StringStream <<
"inferred assignment of ID-dependent value from "
152 const auto *RefVar = dyn_cast<VarDecl>(RefExpr->getDecl());
154 if (IdDepVarsMap.find(RefVar) != IdDepVarsMap.end())
155 StringStream <<
"variable " << RefVar->getNameAsString();
158 const auto *RefField = dyn_cast<FieldDecl>(MemExpr->getMemberDecl());
160 if (IdDepFieldsMap.find(RefField) != IdDepFieldsMap.end())
161 StringStream <<
"member " << RefField->getNameAsString();
163 IdDepVarsMap[PotentialVar] =
164 IdDependencyRecord(PotentialVar, PotentialVar->getBeginLoc(), Message);
167void IdDependentBackwardBranchCheck::saveIdDepFieldFromReference(
168 const DeclRefExpr *RefExpr,
const MemberExpr *MemExpr,
169 const FieldDecl *PotentialField) {
171 if (IdDepFieldsMap.find(PotentialField) != IdDepFieldsMap.end())
174 llvm::raw_string_ostream StringStream(Message);
175 StringStream <<
"inferred assignment of ID-dependent member from "
178 const auto *RefVar = dyn_cast<VarDecl>(RefExpr->getDecl());
180 if (IdDepVarsMap.find(RefVar) != IdDepVarsMap.end())
181 StringStream <<
"variable " << RefVar->getNameAsString();
184 const auto *RefField = dyn_cast<FieldDecl>(MemExpr->getMemberDecl());
185 if (IdDepFieldsMap.find(RefField) != IdDepFieldsMap.end())
186 StringStream <<
"member " << RefField->getNameAsString();
188 IdDepFieldsMap[PotentialField] = IdDependencyRecord(
189 PotentialField, PotentialField->getBeginLoc(), Message);
192IdDependentBackwardBranchCheck::LoopType
193IdDependentBackwardBranchCheck::getLoopType(
const Stmt *Loop) {
194 switch (Loop->getStmtClass()) {
195 case Stmt::DoStmtClass:
197 case Stmt::WhileStmtClass:
199 case Stmt::ForStmtClass:
207 const MatchFinder::MatchResult &Result) {
210 const auto *Variable = Result.Nodes.getNodeAs<VarDecl>(
"tid_dep_var");
211 const auto *
Field = Result.Nodes.getNodeAs<FieldDecl>(
"tid_dep_field");
212 const auto *Statement = Result.Nodes.getNodeAs<Stmt>(
"straight_assignment");
213 const auto *RefExpr = Result.Nodes.getNodeAs<DeclRefExpr>(
"assign_ref_var");
214 const auto *MemExpr = Result.Nodes.getNodeAs<MemberExpr>(
"assign_ref_field");
215 const auto *PotentialVar = Result.Nodes.getNodeAs<VarDecl>(
"pot_tid_var");
216 const auto *PotentialField =
217 Result.Nodes.getNodeAs<FieldDecl>(
"pot_tid_field");
220 if (Statement && (Variable ||
Field)) {
222 saveIdDepVar(Statement, Variable);
224 saveIdDepField(Statement,
Field);
228 if ((RefExpr || MemExpr) && PotentialVar)
229 saveIdDepVarFromReference(RefExpr, MemExpr, PotentialVar);
232 if ((RefExpr || MemExpr) && PotentialField)
233 saveIdDepFieldFromReference(RefExpr, MemExpr, PotentialField);
237 const auto *CondExpr = Result.Nodes.getNodeAs<Expr>(
"cond_expr");
238 const auto *IDCall = Result.Nodes.getNodeAs<CallExpr>(
"id_call");
239 const auto *Loop = Result.Nodes.getNodeAs<Stmt>(
"backward_branch");
242 LoopType
Type = getLoopType(Loop);
245 diag(CondExpr->getBeginLoc(),
246 "backward branch (%select{do|while|for}0 loop) is ID-dependent due "
247 "to ID function call and may cause performance degradation")
252 IdDependencyRecord *IdDepVar = hasIdDepVar(CondExpr);
253 IdDependencyRecord *IdDepField = hasIdDepField(CondExpr);
255 diag(CondExpr->getBeginLoc(),
256 "backward branch (%select{do|while|for}0 loop) is ID-dependent due "
257 "to variable reference to %1 and may cause performance degradation")
258 <<
Type << IdDepVar->VariableDeclaration;
259 diag(IdDepVar->Location, IdDepVar->Message, DiagnosticIDs::Note);
260 }
else if (IdDepField) {
261 diag(CondExpr->getBeginLoc(),
262 "backward branch (%select{do|while|for}0 loop) is ID-dependent due "
263 "to member reference to %1 and may cause performance degradation")
264 <<
Type << IdDepField->FieldDeclaration;
265 diag(IdDepField->Location, IdDepField->Message, DiagnosticIDs::Note);
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
constexpr llvm::StringLiteral Message