clang-tools 23.0.0git
IdDependentBackwardBranchCheck.cpp
Go to the documentation of this file.
1//===----------------------------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
10#include "clang/AST/ASTContext.h"
11#include "clang/ASTMatchers/ASTMatchFinder.h"
12
13using namespace clang::ast_matchers;
14
15namespace clang::tidy::altera {
16
18 // Prototype to identify all variables which hold a thread-variant ID.
19 // First Matcher just finds all the direct assignments of either ID call.
20 const auto ThreadID = expr(hasDescendant(callExpr(callee(functionDecl(
21 anyOf(hasName("get_global_id"), hasName("get_local_id")))))));
22
23 const auto RefVarOrField = forEachDescendant(
24 stmt(anyOf(declRefExpr(to(varDecl())).bind("assign_ref_var"),
25 memberExpr(member(fieldDecl())).bind("assign_ref_field"))));
26
27 Finder->addMatcher(
28 compoundStmt(
29 // Bind on actual get_local/global_id calls.
30 forEachDescendant(
31 stmt(
32 anyOf(declStmt(hasDescendant(varDecl(hasInitializer(ThreadID))
33 .bind("tid_dep_var"))),
34 binaryOperator(
35 isAssignmentOperator(), hasRHS(ThreadID),
36 hasLHS(anyOf(
37 declRefExpr(to(varDecl().bind("tid_dep_var"))),
38 memberExpr(member(
39 fieldDecl().bind("tid_dep_field"))))))))
40 .bind("straight_assignment"))),
41 this);
42
43 // Bind all VarDecls that include an initializer with a variable DeclRefExpr
44 // (in case it is ID-dependent).
45 Finder->addMatcher(
46 stmt(forEachDescendant(
47 varDecl(hasInitializer(RefVarOrField)).bind("pot_tid_var"))),
48 this);
49
50 // Bind all VarDecls that are assigned a value with a variable DeclRefExpr (in
51 // case it is ID-dependent).
52 Finder->addMatcher(
53 stmt(forEachDescendant(
54 binaryOperator(
55 allOf(isAssignmentOperator(), hasRHS(RefVarOrField),
56 hasLHS(anyOf(declRefExpr(to(varDecl().bind("pot_tid_var"))),
57 memberExpr(member(
58 fieldDecl().bind("pot_tid_field")))))))
59 .bind("potential_assignment"))),
60 this);
61
62 // Second Matcher looks for branch statements inside of loops and bind on the
63 // condition expression IF it either calls an ID function or has a variable
64 // DeclRefExpr. DeclRefExprs are checked later to confirm whether the variable
65 // is ID-dependent.
66 const auto CondExpr =
67 expr(anyOf(hasDescendant(callExpr(callee(functionDecl(
68 anyOf(hasName("get_global_id"),
69 hasName("get_local_id")))))
70 .bind("id_call")),
71 hasDescendant(stmt(anyOf(declRefExpr(to(varDecl())),
72 memberExpr(member(fieldDecl())))))))
73 .bind("cond_expr");
74 Finder->addMatcher(stmt(anyOf(forStmt(hasCondition(CondExpr)),
75 doStmt(hasCondition(CondExpr)),
76 whileStmt(hasCondition(CondExpr))))
77 .bind("backward_branch"),
78 this);
79}
80
81const IdDependentBackwardBranchCheck::IdDependencyRecord *
82IdDependentBackwardBranchCheck::hasIdDepVar(const Expr *Expression) {
83 if (!Expression)
84 return nullptr;
85
86 if (const auto *Declaration = dyn_cast<DeclRefExpr>(Expression)) {
87 // It is a DeclRefExpr, so check if it's an ID-dependent variable.
88 const auto *CheckVariable =
89 dyn_cast_if_present<VarDecl>(Declaration->getDecl());
90 if (!CheckVariable)
91 return nullptr;
92 auto FoundVariable = IdDepVarsMap.find(CheckVariable);
93 if (FoundVariable == IdDepVarsMap.end())
94 return nullptr;
95 return &(FoundVariable->second);
96 }
97 for (const auto *Child : Expression->children())
98 if (const auto *ChildExpression = dyn_cast_if_present<Expr>(Child))
99 if (const IdDependencyRecord *Result = hasIdDepVar(ChildExpression))
100 return Result;
101 return nullptr;
102}
103
104const IdDependentBackwardBranchCheck::IdDependencyRecord *
105IdDependentBackwardBranchCheck::hasIdDepField(const Expr *Expression) {
106 if (!Expression)
107 return nullptr;
108
109 if (const auto *MemberExpression = dyn_cast<MemberExpr>(Expression)) {
110 const auto *CheckField =
111 dyn_cast_if_present<FieldDecl>(MemberExpression->getMemberDecl());
112 if (!CheckField)
113 return nullptr;
114 auto FoundField = IdDepFieldsMap.find(CheckField);
115 if (FoundField == IdDepFieldsMap.end())
116 return nullptr;
117 return &(FoundField->second);
118 }
119 for (const auto *Child : Expression->children())
120 if (const auto *ChildExpression = dyn_cast_if_present<Expr>(Child))
121 if (const IdDependencyRecord *Result = hasIdDepField(ChildExpression))
122 return Result;
123 return nullptr;
124}
125
126void IdDependentBackwardBranchCheck::saveIdDepVar(const Stmt *Statement,
127 const VarDecl *Variable) {
128 // Record that this variable is thread-dependent.
129 IdDepVarsMap[Variable] =
130 IdDependencyRecord(Variable, Variable->getBeginLoc(),
131 Twine("assignment of ID-dependent variable ") +
132 Variable->getNameAsString());
133}
134
135void IdDependentBackwardBranchCheck::saveIdDepField(const Stmt *Statement,
136 const FieldDecl *Field) {
137 // Record that this field is thread-dependent.
138 IdDepFieldsMap[Field] = IdDependencyRecord(
139 Field, Statement->getBeginLoc(),
140 Twine("assignment of ID-dependent field ") + Field->getNameAsString());
141}
142
143void IdDependentBackwardBranchCheck::saveIdDepVarFromPotentialReference(
144 const DeclRefExpr *RefExpr, const MemberExpr *MemExpr,
145 const VarDecl *PotentialVar) {
146 // If the variable is already in IdDepVarsMap, ignore it.
147 if (IdDepVarsMap.contains(PotentialVar))
148 return;
149 std::string Message;
150 llvm::raw_string_ostream StringStream(Message);
151 StringStream << "inferred assignment of ID-dependent value from "
152 "ID-dependent ";
153 if (RefExpr) {
154 const auto *RefVar = dyn_cast<VarDecl>(RefExpr->getDecl());
155 // If variable isn't ID-dependent, but RefVar is.
156 if (IdDepVarsMap.contains(RefVar)) {
157 StringStream << "variable " << RefVar->getNameAsString();
158 IdDepVarsMap[PotentialVar] = IdDependencyRecord(
159 PotentialVar, PotentialVar->getBeginLoc(), Message);
160 return;
161 }
162 }
163 if (MemExpr) {
164 const auto *RefField = dyn_cast<FieldDecl>(MemExpr->getMemberDecl());
165 // If variable isn't ID-dependent, but RefField is.
166 if (IdDepFieldsMap.contains(RefField)) {
167 StringStream << "member " << RefField->getNameAsString();
168 IdDepVarsMap[PotentialVar] = IdDependencyRecord(
169 PotentialVar, PotentialVar->getBeginLoc(), Message);
170 }
171 }
172}
173
174void IdDependentBackwardBranchCheck::saveIdDepFieldFromPotentialReference(
175 const Stmt *Statement, const DeclRefExpr *RefExpr,
176 const MemberExpr *MemExpr, const FieldDecl *PotentialField) {
177 // If the field is already in IdDepFieldsMap, ignore it.
178 if (IdDepFieldsMap.contains(PotentialField))
179 return;
180 std::string Message;
181 llvm::raw_string_ostream StringStream(Message);
182 StringStream << "inferred assignment of ID-dependent member from "
183 "ID-dependent ";
184 if (RefExpr) {
185 const auto *RefVar = dyn_cast<VarDecl>(RefExpr->getDecl());
186 // If field isn't ID-dependent, but RefVar is.
187 if (IdDepVarsMap.contains(RefVar)) {
188 StringStream << "variable " << RefVar->getNameAsString();
189 IdDepFieldsMap[PotentialField] =
190 IdDependencyRecord(PotentialField, Statement->getBeginLoc(), Message);
191 return;
192 }
193 }
194 if (MemExpr) {
195 const auto *RefField = dyn_cast<FieldDecl>(MemExpr->getMemberDecl());
196 if (IdDepFieldsMap.contains(RefField)) {
197 StringStream << "member " << RefField->getNameAsString();
198 IdDepFieldsMap[PotentialField] =
199 IdDependencyRecord(PotentialField, Statement->getBeginLoc(), Message);
200 }
201 }
202}
203
204IdDependentBackwardBranchCheck::LoopType
205IdDependentBackwardBranchCheck::getLoopType(const Stmt *Loop) {
206 switch (Loop->getStmtClass()) {
207 case Stmt::DoStmtClass:
208 return DoLoop;
209 case Stmt::WhileStmtClass:
210 return WhileLoop;
211 case Stmt::ForStmtClass:
212 return ForLoop;
213 default:
214 return UnknownLoop;
215 }
216}
217
219 const MatchFinder::MatchResult &Result) {
220 // The first half of the callback only deals with identifying and storing
221 // ID-dependency information into the IdDepVars and IdDepFields maps.
222 const auto *Variable = Result.Nodes.getNodeAs<VarDecl>("tid_dep_var");
223 const auto *Field = Result.Nodes.getNodeAs<FieldDecl>("tid_dep_field");
224 const auto *Statement = Result.Nodes.getNodeAs<Stmt>("straight_assignment");
225 const auto *PotentialAssignment =
226 Result.Nodes.getNodeAs<Stmt>("potential_assignment");
227 const auto *RefExpr = Result.Nodes.getNodeAs<DeclRefExpr>("assign_ref_var");
228 const auto *MemExpr = Result.Nodes.getNodeAs<MemberExpr>("assign_ref_field");
229 const auto *PotentialVar = Result.Nodes.getNodeAs<VarDecl>("pot_tid_var");
230 const auto *PotentialField =
231 Result.Nodes.getNodeAs<FieldDecl>("pot_tid_field");
232
233 // Save variables and fields assigned directly through ID function calls.
234 if (Statement && (Variable || Field)) {
235 if (Variable)
236 saveIdDepVar(Statement, Variable);
237 else if (Field)
238 saveIdDepField(Statement, Field);
239 }
240
241 // Save variables assigned to values of Id-dependent variables and fields.
242 if ((RefExpr || MemExpr) && PotentialVar)
243 saveIdDepVarFromPotentialReference(RefExpr, MemExpr, PotentialVar);
244
245 // Save fields assigned to values of ID-dependent variables and fields.
246 if ((RefExpr || MemExpr) && PotentialField)
247 saveIdDepFieldFromPotentialReference(PotentialAssignment, RefExpr, MemExpr,
248 PotentialField);
249
250 // The second part of the callback deals with checking if a branch inside a
251 // loop is thread dependent.
252 const auto *CondExpr = Result.Nodes.getNodeAs<Expr>("cond_expr");
253 const auto *IDCall = Result.Nodes.getNodeAs<CallExpr>("id_call");
254 const auto *Loop = Result.Nodes.getNodeAs<Stmt>("backward_branch");
255 if (!Loop)
256 return;
257 const LoopType Type = getLoopType(Loop);
258 if (CondExpr) {
259 if (IDCall) { // Conditional expression calls an ID function directly.
260 diag(CondExpr->getBeginLoc(),
261 "backward branch (%select{do|while|for}0 loop) is ID-dependent due "
262 "to ID function call and may cause performance degradation")
263 << Type;
264 return;
265 }
266 // Conditional expression has DeclRefExpr(s), check ID-dependency.
267 const IdDependencyRecord *IdDepVar = hasIdDepVar(CondExpr);
268 const IdDependencyRecord *IdDepField = hasIdDepField(CondExpr);
269 if (IdDepVar) {
270 diag(CondExpr->getBeginLoc(),
271 "backward branch (%select{do|while|for}0 loop) is ID-dependent due "
272 "to variable reference to %1 and may cause performance degradation")
273 << Type << IdDepVar->VariableDeclaration;
274 diag(IdDepVar->Location, IdDepVar->Message, DiagnosticIDs::Note);
275 } else if (IdDepField) {
276 diag(CondExpr->getBeginLoc(),
277 "backward branch (%select{do|while|for}0 loop) is ID-dependent due "
278 "to member reference to %1 and may cause performance degradation")
279 << Type << IdDepField->FieldDeclaration;
280 diag(IdDepField->Location, IdDepField->Message, DiagnosticIDs::Note);
281 }
282 }
283}
284
285} // namespace clang::tidy::altera
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
void registerMatchers(ast_matchers::MatchFinder *Finder) override