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
 |