Skip to content

Commit

Permalink
Merge pull request #1 from kokkos/implicit-this
Browse files Browse the repository at this point in the history
Initial Setup and Implicit this Check
  • Loading branch information
Drew Lewis authored Jun 10, 2020
2 parents 2fea3fe + 1a8d457 commit a35ba11
Show file tree
Hide file tree
Showing 13 changed files with 516 additions and 164 deletions.
2 changes: 2 additions & 0 deletions clang-tools-extra/clang-tidy/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ add_subdirectory(darwin)
add_subdirectory(fuchsia)
add_subdirectory(google)
add_subdirectory(hicpp)
add_subdirectory(kokkos)
add_subdirectory(linuxkernel)
add_subdirectory(llvm)
add_subdirectory(llvmlibc)
Expand All @@ -75,6 +76,7 @@ set(ALL_CLANG_TIDY_CHECKS
clangTidyFuchsiaModule
clangTidyGoogleModule
clangTidyHICPPModule
clangTidyKokkosModule
clangTidyLinuxKernelModule
clangTidyLLVMModule
clangTidyLLVMLibcModule
Expand Down
9 changes: 9 additions & 0 deletions clang-tools-extra/clang-tidy/ClangTidyForceLinker.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
//===- ClangTidyForceLinker.h - clang-tidy --------------------------------===//
//
// Copyright 2020 National Technology & Engineering Solutions of Sandia, LLC
// (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S.
// Government retains certain rights in this software.
//
// 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
Expand Down Expand Up @@ -65,6 +69,11 @@ extern volatile int HICPPModuleAnchorSource;
static int LLVM_ATTRIBUTE_UNUSED HICPPModuleAnchorDestination =
HICPPModuleAnchorSource;

// This anchor is used to force the linker to link the KokkosModule.
extern volatile int KokkosModuleAnchorSource;
static int LLVM_ATTRIBUTE_UNUSED KokkosModuleAnchorDestination =
KokkosModuleAnchorSource;

// This anchor is used to force the linker to link the LinuxKernelModule.
extern volatile int LinuxKernelModuleAnchorSource;
static int LLVM_ATTRIBUTE_UNUSED LinuxKernelModuleAnchorDestination =
Expand Down
4 changes: 4 additions & 0 deletions clang-tools-extra/clang-tidy/add_new_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ def write_header(module_path, module, namespace, check_name, check_name_camel):
f.write('*- C++ -*-===//')
f.write("""
//
// Copyright 2020 National Technology & Engineering Solutions of Sandia,
// LLC (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S.
// Government retains certain rights in this software.
//
// 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
Expand Down
16 changes: 16 additions & 0 deletions clang-tools-extra/clang-tidy/kokkos/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
set(LLVM_LINK_COMPONENTS support)

add_clang_library(clangTidyKokkosModule
ImplicitThisCaptureCheck.cpp
KokkosMatchers.cpp
KokkosTidyModule.cpp

LINK_LIBS
clangAnalysis
clangAST
clangASTMatchers
clangBasic
clangLex
clangTidy
clangTidyUtils
)
94 changes: 94 additions & 0 deletions clang-tools-extra/clang-tidy/kokkos/ImplicitThisCaptureCheck.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
//===--- ImplicitThisCaptureCheck.cpp - clang-tidy ------------------------===//
//
// Copyright 2020 National Technology & Engineering Solutions of Sandia, LLC
// (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S.
// Government retains certain rights in this software.
//
// 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 "ImplicitThisCaptureCheck.h"
#include "KokkosMatchers.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "llvm/ADT/Optional.h"

using namespace clang::ast_matchers;

namespace clang {
namespace tidy {
namespace kokkos {

namespace {
llvm::Optional<SourceLocation> capturesThis(CXXRecordDecl const *CRD) {
if (!CRD->isLambda()) {
return llvm::None;
}

for (auto const &Capture : CRD->captures()) {
if (Capture.capturesThis()) {
return llvm::Optional<SourceLocation>(Capture.getLocation());
}
}

return llvm::None;
}

} // namespace

ImplicitThisCaptureCheck::ImplicitThisCaptureCheck(
StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {
CheckIfExplicitHost = std::stoi(Options.get("CheckIfExplicitHost", "0"));
HostTypeDefRegex =
Options.get("HostTypeDefRegex", "Kokkos::DefaultHostExecutionSpace");
}

void ImplicitThisCaptureCheck::storeOptions(
ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "CheckIfExplicitHost",
std::to_string(CheckIfExplicitHost));
Options.store(Opts, "HostTypeDefRegex", HostTypeDefRegex);
}

void ImplicitThisCaptureCheck::registerMatchers(MatchFinder *Finder) {
auto isAllowedPolicy = expr(hasType(
cxxRecordDecl(matchesName("::Impl::ThreadVectorRangeBoundariesStruct.*|::"
"Impl::TeamThreadRangeBoundariesStruct.*"))));

auto Lambda = expr(hasType(cxxRecordDecl(isLambda()).bind("Lambda")));

Finder->addMatcher(
callExpr(isKokkosParallelCall(),
hasAnyArgument(Lambda), unless(hasAnyArgument(isAllowedPolicy)))
.bind("x"),
this);
}

void ImplicitThisCaptureCheck::check(
const MatchFinder::MatchResult &Result) {
auto const *CE = Result.Nodes.getNodeAs<CallExpr>("x");

if (CheckIfExplicitHost) {
if (explicitlyUsingHostExecutionSpace(CE, HostTypeDefRegex)) {
return;
}
}

auto const *Lambda = Result.Nodes.getNodeAs<CXXRecordDecl>("Lambda");
auto CaptureLocation = capturesThis(Lambda);
if (CaptureLocation) {
diag(Lambda->getBeginLoc(), "Lambda passed to %0 implicitly captures this.")
<< CE->getDirectCallee()->getName();
diag(CaptureLocation.getValue(), "the captured variable is used here.",
DiagnosticIDs::Note);
diag(CE->getBeginLoc(), "Kokkos call here.", DiagnosticIDs::Note);
}
}

} // namespace kokkos
} // namespace tidy
} // namespace clang
41 changes: 41 additions & 0 deletions clang-tools-extra/clang-tidy/kokkos/ImplicitThisCaptureCheck.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//===--- ImplicitThisCaptureCheck.h - clang-tidy ----------------*- C++ -*-===//
//
// Copyright 2020 National Technology & Engineering Solutions of Sandia, LLC
// (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S.
// Government retains certain rights in this software.
//
// 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_KOKKOS_IMPLICITTHISCAPTURECHECK_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_KOKKOS_IMPLICITTHISCAPTURECHECK_H

#include "../ClangTidyCheck.h"

#include <string>

namespace clang {
namespace tidy {
namespace kokkos {

/// Check to detect when a lambda passed to a ::Kokkos::parallel_* implicitly
/// captures the this pointer
class ImplicitThisCaptureCheck : public ClangTidyCheck {
public:
ImplicitThisCaptureCheck(StringRef Name, ClangTidyContext *Context);
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
private:
int CheckIfExplicitHost;
std::string HostTypeDefRegex;
};

} // namespace kokkos
} // namespace tidy
} // namespace clang

#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_KOKKOS_IMPLICITTHISCAPTURECHECK_H
54 changes: 54 additions & 0 deletions clang-tools-extra/clang-tidy/kokkos/KokkosMatchers.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
//===--- KokkosMatchers.cpp - clang-tidy ------------------------===//
//
// Copyright 2020 National Technology & Engineering Solutions of Sandia, LLC
// (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S.
// Government retains certain rights in this software.
//
// 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 "KokkosMatchers.h"

namespace clang {
namespace tidy {
namespace kokkos {

bool explicitlyUsingHostExecutionSpace(CallExpr const *CE,
std::string const &RegexString) {
using namespace clang::ast_matchers;
auto &Ctx = CE->getCalleeDecl()->getASTContext();

// We will assume that any policy where the user might explicitly ask for the
// host space inherits from Impl::PolicyTraits
auto FilterArgs =
hasAnyArgument(expr(hasType(cxxRecordDecl(isDerivedFrom(cxxRecordDecl(
matchesName("Impl::PolicyTraits"))))))
.bind("expr"));

// We have to jump through some hoops to find this, if we just looked at the
// template type of the Policy constructor we lose the sugar and instead of
// Kokkos::DefaultHostExecutionSpace we get what the ever the typedef was set
// to such as Kokkos::Serial, preventing us from figuring out if the user
// actually asked for a host space specifically or just happens to have a
// host space as the default space.
llvm::Regex Reg(RegexString);
auto BNs = match(callExpr(FilterArgs).bind("CE"), *CE, Ctx);
for (auto &BN : BNs) {
if (auto const *E = BN.getNodeAs<Expr>("expr")) {
if (auto const *TST = E->getType()->getAs<TemplateSpecializationType>()) {
if (Reg.match(TST->getArg(0).getAsType().getAsString())) {
return true;
}
}
}
}

return false;
}

} // namespace kokkos
} // namespace tidy
} // namespace clang
52 changes: 52 additions & 0 deletions clang-tools-extra/clang-tidy/kokkos/KokkosMatchers.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
//===--- KokkosMatchers.h - clang-tidy ------------------------===//
//
// Copyright 2020 National Technology & Engineering Solutions of Sandia, LLC
// (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S.
// Government retains certain rights in this software.
//
// 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_KOKKOS_KOKKOSMATCHERS_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_KOKKOS_KOKKOSMATCHERS_H

#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include <string>

namespace clang {
namespace tidy {
namespace kokkos {

AST_MATCHER_FUNCTION(ast_matchers::internal::Matcher<FunctionDecl>,
kokkosParallelXFunctionDecl) {
using namespace clang::ast_matchers;
return functionDecl(matchesName("::Kokkos::parallel_.*"));
}

AST_MATCHER(CallExpr, isKokkosParallelCall) {
using namespace clang::ast_matchers;
if (auto const *FD = Node.getDirectCallee()) {
std::string Name = FD->getQualifiedNameAsString();
StringRef SR(Name);
if (SR.startswith("::Kokkos::parallel_")) {
return true;
}
}

return false;
}

bool explicitlyUsingHostExecutionSpace(CallExpr const *CE,
std::string const &RegexString);


} // namespace kokkos
} // namespace tidy
} // namespace clang

#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_KOKKOS_KOKKOSMATCHERS_H
41 changes: 41 additions & 0 deletions clang-tools-extra/clang-tidy/kokkos/KokkosTidyModule.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//===--- KokkosTidyModule.cpp - clang-tidy ----------------------------------===//
//
// Copyright 2020 National Technology & Engineering Solutions of Sandia, LLC
// (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S.
// Government retains certain rights in this software.
//
// 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 "../ClangTidy.h"
#include "../ClangTidyModule.h"
#include "../ClangTidyModuleRegistry.h"
#include "ImplicitThisCaptureCheck.h"

namespace clang {
namespace tidy {
namespace kokkos {

class KokkosModule : public ClangTidyModule {
public:
void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
CheckFactories.registerCheck<ImplicitThisCaptureCheck>(
"kokkos-implicit-this-capture");
}
};

} // namespace kokkos

// Register the DarwinTidyModule using this statically initialized variable.
static ClangTidyModuleRegistry::Add<kokkos::KokkosModule>
X("kokkos-module", "Adds Kokkos specific linting checks.");

// This anchor is used to force the linker to link in the generated object file
// and thus register the KokkosModule.
volatile int KokkosModuleAnchorSource = 0;

} // namespace tidy
} // namespace clang
5 changes: 5 additions & 0 deletions clang-tools-extra/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,11 @@ New checks
Flags use of the `C` standard library functions ``memset``, ``memcpy`` and
``memcmp`` and similar derivatives on non-trivial types.

- New :doc:`kokkos-implicit-this-capture
<clang-tidy/checks/kokkos-implicit-this-capture>` check.

New check for implicit this capture detection

- New :doc:`llvmlibc-callee-namespace
<clang-tidy/checks/llvmlibc-callee-namespace>` check.

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
.. title:: clang-tidy - kokkos-implicit-this-capture

kokkos-implicit-this-capture
============================

The implicit-this-capture check checks if a lambda passed to any of
parallel_reduce, parallel_for, or parallel_scan functions captures the this
pointer.

Example of a class that should trigger the check
.. code-block:: c++

class Foo {
int x = 0;

void operator()(){
Kokkos::parallel_for(10, KOKKOS_LAMBDA(int y){ printf("%d", x+y)});
}
};
Loading

0 comments on commit a35ba11

Please sign in to comment.