njames93 updated this revision to Diff 398404.
njames93 marked 4 inline comments as done.
njames93 added a comment.

Address comments


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D116875/new/

https://reviews.llvm.org/D116875

Files:
  clang-tools-extra/clang-tidy/performance/CMakeLists.txt
  clang-tools-extra/clang-tidy/performance/InefficientArrayTraversalCheck.cpp
  clang-tools-extra/clang-tidy/performance/InefficientArrayTraversalCheck.h
  clang-tools-extra/clang-tidy/performance/PerformanceTidyModule.cpp
  clang-tools-extra/docs/ReleaseNotes.rst
  clang-tools-extra/docs/clang-tidy/checks/list.rst
  
clang-tools-extra/docs/clang-tidy/checks/performance-inefficient-array-traversal.rst
  
clang-tools-extra/test/clang-tidy/checkers/performance-inefficient-array-traversal.cpp

Index: clang-tools-extra/test/clang-tidy/checkers/performance-inefficient-array-traversal.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/performance-inefficient-array-traversal.cpp
@@ -0,0 +1,84 @@
+// RUN: %check_clang_tidy %s performance-inefficient-array-traversal %t
+
+constexpr unsigned Rows = 10U;
+constexpr unsigned Cols = 16U;
+
+int Arr[Rows][Cols];
+int *PtrArr[Cols];
+int **Ptr;
+
+void foo();
+
+void warned() {
+  for (unsigned I = 0; I < Cols; ++I) {
+    for (unsigned J = 0; J < Rows; ++J) {
+      Arr[J][I]++;
+      // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: Nonsequential array traversal can harm performance
+      // CHECK-MESSAGES: :[[@LINE-3]]:5: note: Row index 'J' incremented in this loop
+      // CHECK-MESSAGES: :[[@LINE-5]]:3: note: Column index 'I' incremented in this loop
+    }
+  }
+  for (unsigned I = 0; I < Cols; ++I)
+    for (unsigned J = 0; J < Rows; ++J)
+      Arr[J][I]++;
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: Nonsequential array traversal can harm performance
+
+  // Ensure it works on array access that use pointers
+  for (unsigned I = 0; I < Cols; ++I)
+    for (unsigned J = 0; J < Rows; ++J)
+      PtrArr[J][I]++;
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: Nonsequential array traversal can harm performance
+
+  for (unsigned I = 0; I < Cols; ++I)
+    for (unsigned J = 0; J < Rows; ++J)
+      Ptr[J][I]++;
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: Nonsequential array traversal can harm performance
+
+  // Detect += 1 as the loop incriment
+  for (unsigned I = 0; I < Cols; I += 1)
+    for (unsigned J = 0; J < Rows; J += 1)
+      Arr[J][I]++;
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: Nonsequential array traversal can harm performance
+
+  // Still warn on this as calls inside the inner loop shouldn't complicate a fix.
+  for (unsigned I = 0; I < Cols; ++I) {
+    for (unsigned J = 0; J < Rows; ++J) {
+      foo();
+      Arr[J][I]++;
+      // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: Nonsequential array traversal can harm performance
+      foo();
+    }
+  }
+}
+
+void ignored() {
+  // Correct traversal.
+  for (unsigned I = 0; I < Rows; ++I) {
+    for (unsigned J = 0; J < Cols; ++J) {
+      Arr[I][J]++;
+    }
+  }
+  for (unsigned I = 0; I < Rows; ++I) {
+    for (unsigned J = 0; J < Cols; ++J) {
+      PtrArr[I][J]++;
+    }
+  }
+  // Don'w warn on these cases as extra code inside the outer loop could complicate a fix.
+  for (unsigned I = 0; I < Cols; ++I) {
+    foo();
+    for (unsigned J = 0; J < Rows; ++J) {
+      Arr[J][I]++;
+    }
+  }
+  for (unsigned I = 0; I < Cols; ++I) {
+    for (unsigned J = 0; J < Rows; ++J) {
+      Arr[J][I]++;
+    }
+    foo();
+  }
+
+  // Nested loop increments incorrect variable, don't warn on the traversal.
+  for (unsigned I = 0; I < Cols; ++I)
+    for (unsigned J = 0; J < Rows; ++I)
+      Arr[J][I]++;
+}
Index: clang-tools-extra/docs/clang-tidy/checks/performance-inefficient-array-traversal.rst
===================================================================
--- /dev/null
+++ clang-tools-extra/docs/clang-tidy/checks/performance-inefficient-array-traversal.rst
@@ -0,0 +1,24 @@
+.. title:: clang-tidy - performance-inefficient-array-traversal
+
+performance-inefficient-array-traversal
+=======================================
+
+Detects nested ``for`` loops used to traverse a 2D array where the row index
+is iterated on the inner loop.
+
+.. code-block:: c++
+
+   for (int X = 0; X < Columns; ++X)
+     for (int Y = 0; Y < Rows; ++Y)
+       Array[Y][X]++;
+
+This array access pattern results in nonsequential data access which is cache 
+unfriendly and can prevent auto vectorization optimizations.
+
+A much faster version of the above loop would be
+
+.. code-block:: c++
+
+   for (int Y = 0; Y < Rows; ++Y)
+     for (int X = 0; X < Columns; ++X)
+       Array[Y][X]++;
Index: clang-tools-extra/docs/clang-tidy/checks/list.rst
===================================================================
--- clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -273,6 +273,7 @@
    `performance-for-range-copy <performance-for-range-copy.html>`_, "Yes"
    `performance-implicit-conversion-in-loop <performance-implicit-conversion-in-loop.html>`_,
    `performance-inefficient-algorithm <performance-inefficient-algorithm.html>`_, "Yes"
+   `performance-inefficient-array-traversal <performance-inefficient-array-traversal.html>`_,
    `performance-inefficient-string-concatenation <performance-inefficient-string-concatenation.html>`_,
    `performance-inefficient-vector-operation <performance-inefficient-vector-operation.html>`_, "Yes"
    `performance-move-const-arg <performance-move-const-arg.html>`_, "Yes"
Index: clang-tools-extra/docs/ReleaseNotes.rst
===================================================================
--- clang-tools-extra/docs/ReleaseNotes.rst
+++ clang-tools-extra/docs/ReleaseNotes.rst
@@ -111,6 +111,12 @@
 
   Reports identifier with unicode right-to-left characters.
 
+- New :doc:`performance-inefficient-array-traversal
+  <clang-tidy/checks/performance-inefficient-array-traversal>` check.
+
+  Detects nested ``for`` loops used to traverse a 2D array where the row index
+  is iterated on the inner loop.
+
 - New :doc:`readability-container-data-pointer
   <clang-tidy/checks/readability-container-data-pointer>` check.
 
Index: clang-tools-extra/clang-tidy/performance/PerformanceTidyModule.cpp
===================================================================
--- clang-tools-extra/clang-tidy/performance/PerformanceTidyModule.cpp
+++ clang-tools-extra/clang-tidy/performance/PerformanceTidyModule.cpp
@@ -13,6 +13,7 @@
 #include "ForRangeCopyCheck.h"
 #include "ImplicitConversionInLoopCheck.h"
 #include "InefficientAlgorithmCheck.h"
+#include "InefficientArrayTraversalCheck.h"
 #include "InefficientStringConcatenationCheck.h"
 #include "InefficientVectorOperationCheck.h"
 #include "MoveConstArgCheck.h"
@@ -40,6 +41,8 @@
         "performance-implicit-conversion-in-loop");
     CheckFactories.registerCheck<InefficientAlgorithmCheck>(
         "performance-inefficient-algorithm");
+    CheckFactories.registerCheck<InefficientArrayTraversalCheck>(
+        "performance-inefficient-array-traversal");
     CheckFactories.registerCheck<InefficientStringConcatenationCheck>(
         "performance-inefficient-string-concatenation");
     CheckFactories.registerCheck<InefficientVectorOperationCheck>(
Index: clang-tools-extra/clang-tidy/performance/InefficientArrayTraversalCheck.h
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-tidy/performance/InefficientArrayTraversalCheck.h
@@ -0,0 +1,38 @@
+//===--- InefficientArrayTraversalCheck.h - clang-tidy ----------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_INEFFICIENTARRAYTRAVERSALCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_INEFFICIENTARRAYTRAVERSALCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang {
+namespace tidy {
+namespace performance {
+
+/// Detects nested for loops used to traverse a 2D array where the row index is
+/// iterated on the inner loop.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/performance-inefficient-array-traversal.html
+class InefficientArrayTraversalCheck : public ClangTidyCheck {
+public:
+  InefficientArrayTraversalCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+  llvm::Optional<TraversalKind> getCheckTraversalKind() const override {
+    return TK_IgnoreUnlessSpelledInSource;
+  }
+};
+
+} // namespace performance
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_INEFFICIENTARRAYTRAVERSALCHECK_H
Index: clang-tools-extra/clang-tidy/performance/InefficientArrayTraversalCheck.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-tidy/performance/InefficientArrayTraversalCheck.cpp
@@ -0,0 +1,106 @@
+//===--- InefficientArrayTraversalCheck.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 "InefficientArrayTraversalCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/ASTMatchers/ASTMatchersInternal.h"
+#include "clang/Basic/DiagnosticIDs.h"
+#include "llvm/ADT/StringRef.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace performance {
+
+namespace {
+AST_MATCHER_P(Stmt, hasSingleStmt, ast_matchers::internal::Matcher<Stmt>,
+              InnerMatch) {
+  clang::ast_matchers::internal::BoundNodesTreeBuilder Copy(*Builder);
+  if (InnerMatch.matches(Node, Finder, &Copy)) {
+    *Builder = Copy;
+    return true;
+  }
+  if (const auto *Res = dyn_cast<CompoundStmt>(&Node)) {
+    return Res->size() == 1 &&
+           InnerMatch.matches(*Res->body_front(), Finder, Builder);
+  }
+  return false;
+}
+ast_matchers::internal::Matcher<Stmt>
+stmtOrCompound(ast_matchers::internal::Matcher<Stmt> Inner) {
+  return anyOf(Inner, compoundStmt(hasAnySubstatement(Inner)));
+}
+} // namespace
+
+static const char InnerVar[] = "InnerVar";
+static const char OuterVar[] = "OuterVar";
+static const char InnerLoop[] = "InnerLoop";
+static const char OuterLoop[] = "OuterLoop";
+static const char ArrayAccess[] = "ArrayAccess";
+
+void InefficientArrayTraversalCheck::registerMatchers(MatchFinder *Finder) {
+  auto NestedLoopMatcher = [](auto InnerLoopBody) {
+    auto ForLoopBase = [](auto BodyMatcher, bool Inner) {
+      StringRef Var = Inner ? InnerVar : OuterVar;
+      auto ToDecl = declRefExpr(hasDeclaration(equalsBoundNode(Var.str())));
+      return forStmt(hasLoopInit(declStmt(hasSingleDecl(
+                         varDecl(hasType(isInteger())).bind(Var)))),
+                     hasCondition(binaryOperator(hasEitherOperand(ToDecl))),
+                     // TODO: Add support for x = x [+-] 1
+                     hasIncrement(anyOf(
+                         unaryOperator(hasUnaryOperand(ToDecl),
+                                       hasAnyOperatorName("++", "--")),
+                         binaryOperator(hasAnyOperatorName("+=", "-="),
+                                        hasLHS(ToDecl),
+                                        hasRHS(integerLiteral(equals(1)))))),
+                     hasBody(BodyMatcher))
+          .bind(Inner ? InnerLoop : OuterLoop);
+    };
+    return ForLoopBase(
+        hasSingleStmt(ForLoopBase(stmtOrCompound(InnerLoopBody), true)), false);
+  };
+  auto ArrayExprMatcher = expr(hasDescendant(
+      arraySubscriptExpr(
+          hasIndex(declRefExpr(hasDeclaration(equalsBoundNode(OuterVar)))),
+          hasBase(arraySubscriptExpr(hasIndex(
+              declRefExpr(hasDeclaration(equalsBoundNode(InnerVar)))))))
+          .bind(ArrayAccess)));
+  Finder->addMatcher(NestedLoopMatcher(ArrayExprMatcher), this);
+  // TODO: Add a matcher that works on flattened arrays -
+  // Array[InnerVar * OuterExtent + OuterVar]
+  // TODO: Add a matcher that works for containers
+  // VectorOfVectors[InnerVar][OuterVar]
+}
+
+void InefficientArrayTraversalCheck::check(
+    const MatchFinder::MatchResult &Result) {
+  const auto *Array = Result.Nodes.getNodeAs<ArraySubscriptExpr>(ArrayAccess);
+  const auto *InnerLoopStmt = Result.Nodes.getNodeAs<ForStmt>(InnerLoop);
+  const auto *OuterLoopStmt = Result.Nodes.getNodeAs<ForStmt>(OuterLoop);
+  const auto *InnerVariable = Result.Nodes.getNodeAs<VarDecl>(InnerVar);
+  const auto *OuterVariable = Result.Nodes.getNodeAs<VarDecl>(OuterVar);
+  diag(Array->getBeginLoc(),
+       "Nonsequential array traversal can harm performance");
+  diag(InnerLoopStmt->getBeginLoc(), "Row index %0 incremented in this loop",
+       DiagnosticIDs::Note)
+      << InnerVariable
+      << SourceRange(InnerLoopStmt->getBeginLoc(),
+                     InnerLoopStmt->getRParenLoc());
+  diag(OuterLoopStmt->getBeginLoc(), "Column index %0 incremented in this loop",
+       DiagnosticIDs::Note)
+      << OuterVariable
+      << SourceRange(OuterLoopStmt->getBeginLoc(),
+                     OuterLoopStmt->getRParenLoc());
+}
+
+} // namespace performance
+} // namespace tidy
+} // namespace clang
Index: clang-tools-extra/clang-tidy/performance/CMakeLists.txt
===================================================================
--- clang-tools-extra/clang-tidy/performance/CMakeLists.txt
+++ clang-tools-extra/clang-tidy/performance/CMakeLists.txt
@@ -8,6 +8,7 @@
   ForRangeCopyCheck.cpp
   ImplicitConversionInLoopCheck.cpp
   InefficientAlgorithmCheck.cpp
+  InefficientArrayTraversalCheck.cpp
   InefficientStringConcatenationCheck.cpp
   InefficientVectorOperationCheck.cpp
   MoveConstArgCheck.cpp
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to