aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/libs/clang16/tools/extra/clang-tidy/altera/IdDependentBackwardBranchCheck.cpp
blob: 66e6d97f500ce45a4473b3e8e958ce04dfb136a4 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
//===--- 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::tidy::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 clang::tidy::altera