diff options
author | Alexander Droste <alexander.ra.droste@gmail.com> | 2016-08-12 19:30:31 +0000 |
---|---|---|
committer | Alexander Droste <alexander.ra.droste@gmail.com> | 2016-08-12 19:30:31 +0000 |
commit | 1512f9a0f96f2795ca497099d3d39ed30cdaea33 (patch) | |
tree | 2a882a91b985b237d94b92dcd61b488060170dcf /clang-tools-extra | |
parent | af32ccb195ece1a25511d1d35d8c14ce1b58ee9b (diff) | |
download | llvm-1512f9a0f96f2795ca497099d3d39ed30cdaea33.zip llvm-1512f9a0f96f2795ca497099d3d39ed30cdaea33.tar.gz llvm-1512f9a0f96f2795ca497099d3d39ed30cdaea33.tar.bz2 |
[clang-tidy] MPIBufferDerefCheck
...
This check verifies if a buffer passed to an MPI (Message Passing Interface)
function is sufficiently dereferenced. Buffers should be passed as a single
pointer or array. As MPI function signatures specify void * for their buffer
types, insufficiently dereferenced buffers can be passed, like for example
as double pointers or multidimensional arrays, without a compiler warning
emitted.
Instructions on how to apply the check can be found at:
https://github.com/0ax1/MPI-Checker/tree/master/examples
Reviewers: Haojian Wu
Differential revision: https://reviews.llvm.org/D22729
llvm-svn: 278553
Diffstat (limited to 'clang-tools-extra')
9 files changed, 274 insertions, 0 deletions
diff --git a/clang-tools-extra/clang-tidy/mpi/BufferDerefCheck.cpp b/clang-tools-extra/clang-tidy/mpi/BufferDerefCheck.cpp new file mode 100644 index 0000000..8e2c0e3 --- /dev/null +++ b/clang-tools-extra/clang-tidy/mpi/BufferDerefCheck.cpp @@ -0,0 +1,132 @@ +//===--- BufferDerefCheck.cpp - clang-tidy---------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "BufferDerefCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/StaticAnalyzer/Checkers/MPIFunctionClassifier.h" +#include "clang/Tooling/FixIt.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace mpi { + +void BufferDerefCheck::registerMatchers(MatchFinder *Finder) { + Finder->addMatcher(callExpr().bind("CE"), this); +} + +void BufferDerefCheck::check(const MatchFinder::MatchResult &Result) { + static ento::mpi::MPIFunctionClassifier FuncClassifier(*Result.Context); + const auto *CE = Result.Nodes.getNodeAs<CallExpr>("CE"); + if (!CE->getDirectCallee()) + return; + + const IdentifierInfo *Identifier = CE->getDirectCallee()->getIdentifier(); + if (!Identifier || !FuncClassifier.isMPIType(Identifier)) + return; + + // These containers are used, to capture the type and expression of a buffer. + SmallVector<const Type *, 1> BufferTypes; + SmallVector<const Expr *, 1> BufferExprs; + + // Adds the type and expression of a buffer that is used in the MPI call + // expression to the captured containers. + auto addBuffer = [&CE, &Result, &BufferTypes, + &BufferExprs](const size_t BufferIdx) { + // Skip null pointer constants and in place 'operators'. + if (CE->getArg(BufferIdx)->isNullPointerConstant( + *Result.Context, Expr::NPC_ValueDependentIsNull) || + tooling::fixit::getText(*CE->getArg(BufferIdx), *Result.Context) == + "MPI_IN_PLACE") + return; + + const Expr *ArgExpr = CE->getArg(BufferIdx); + if (!ArgExpr) + return; + const Type *ArgType = ArgExpr->IgnoreImpCasts()->getType().getTypePtr(); + if (!ArgType) + return; + BufferExprs.push_back(ArgExpr); + BufferTypes.push_back(ArgType); + }; + + // Collect buffer types and argument expressions for all buffers used in the + // MPI call expression. The number passed to the lambda corresponds to the + // argument index of the currently verified MPI function call. + if (FuncClassifier.isPointToPointType(Identifier)) { + addBuffer(0); + } else if (FuncClassifier.isCollectiveType(Identifier)) { + if (FuncClassifier.isReduceType(Identifier)) { + addBuffer(0); + addBuffer(1); + } else if (FuncClassifier.isScatterType(Identifier) || + FuncClassifier.isGatherType(Identifier) || + FuncClassifier.isAlltoallType(Identifier)) { + addBuffer(0); + addBuffer(3); + } else if (FuncClassifier.isBcastType(Identifier)) { + addBuffer(0); + } + } + + checkBuffers(BufferTypes, BufferExprs); +} + +void BufferDerefCheck::checkBuffers(ArrayRef<const Type *> BufferTypes, + ArrayRef<const Expr *> BufferExprs) { + for (size_t i = 0; i < BufferTypes.size(); ++i) { + unsigned IndirectionCount = 0; + const Type *BufferType = BufferTypes[i]; + llvm::SmallVector<IndirectionType, 1> Indirections; + + // Capture the depth and types of indirections for the passed buffer. + while (true) { + if (BufferType->isPointerType()) { + BufferType = BufferType->getPointeeType().getTypePtr(); + Indirections.push_back(IndirectionType::Pointer); + } else if (BufferType->isArrayType()) { + BufferType = BufferType->getArrayElementTypeNoTypeQual(); + Indirections.push_back(IndirectionType::Array); + } else { + break; + } + ++IndirectionCount; + } + + if (IndirectionCount > 1) { + // Referencing an array with '&' is valid, as this also points to the + // beginning of the array. + if (IndirectionCount == 2 && + Indirections[0] == IndirectionType::Pointer && + Indirections[1] == IndirectionType::Array) + return; + + // Build the indirection description in reverse order of discovery. + std::string IndirectionDesc; + for (auto It = Indirections.rbegin(); It != Indirections.rend(); ++It) { + if (!IndirectionDesc.empty()) + IndirectionDesc += "->"; + if (*It == IndirectionType::Pointer) { + IndirectionDesc += "pointer"; + } else { + IndirectionDesc += "array"; + } + } + + const auto Loc = BufferExprs[i]->getSourceRange().getBegin(); + diag(Loc, "buffer is insufficiently dereferenced: %0") << IndirectionDesc; + } + } +} + +} // namespace mpi +} // namespace tidy +} // namespace clang diff --git a/clang-tools-extra/clang-tidy/mpi/BufferDerefCheck.h b/clang-tools-extra/clang-tidy/mpi/BufferDerefCheck.h new file mode 100644 index 0000000..8490fa1 --- /dev/null +++ b/clang-tools-extra/clang-tidy/mpi/BufferDerefCheck.h @@ -0,0 +1,51 @@ +//===--- BufferDerefCheck.h - clang-tidy-------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MPI_BUFFER_DEREF_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MPI_BUFFER_DEREF_H + +#include "../ClangTidy.h" + +namespace clang { +namespace tidy { +namespace mpi { + +/// This check verifies if a buffer passed to an MPI (Message Passing Interface) +/// function is sufficiently dereferenced. Buffers should be passed as a single +/// pointer or array. As MPI function signatures specify void * for their buffer +/// types, insufficiently dereferenced buffers can be passed, like for example +/// as double pointers or multidimensional arrays, without a compiler warning +/// emitted. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/mpi-buffer-deref.html +class BufferDerefCheck : public ClangTidyCheck { +public: + BufferDerefCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + +private: + /// Checks for all buffers in an MPI call if they are sufficiently + /// dereferenced. + /// + /// \param BufferTypes buffer types + /// \param BufferExprs buffer arguments as expressions + void checkBuffers(ArrayRef<const Type *> BufferTypes, + ArrayRef<const Expr *> BufferExprs); + + enum class IndirectionType : unsigned char { Pointer, Array }; +}; + +} // namespace mpi +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MPI_BUFFER_DEREF_H diff --git a/clang-tools-extra/clang-tidy/mpi/CMakeLists.txt b/clang-tools-extra/clang-tidy/mpi/CMakeLists.txt index 15bfb33..3658434 100644 --- a/clang-tools-extra/clang-tidy/mpi/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/mpi/CMakeLists.txt @@ -1,6 +1,7 @@ set(LLVM_LINK_COMPONENTS support) add_clang_library(clangTidyMPIModule + BufferDerefCheck.cpp MPITidyModule.cpp TypeMismatchCheck.cpp diff --git a/clang-tools-extra/clang-tidy/mpi/MPITidyModule.cpp b/clang-tools-extra/clang-tidy/mpi/MPITidyModule.cpp index babf7b6..55e187d 100644 --- a/clang-tools-extra/clang-tidy/mpi/MPITidyModule.cpp +++ b/clang-tools-extra/clang-tidy/mpi/MPITidyModule.cpp @@ -10,6 +10,7 @@ #include "../ClangTidy.h" #include "../ClangTidyModule.h" #include "../ClangTidyModuleRegistry.h" +#include "BufferDerefCheck.h" #include "TypeMismatchCheck.h" namespace clang { @@ -19,6 +20,7 @@ namespace mpi { class MPIModule : public ClangTidyModule { public: void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override { + CheckFactories.registerCheck<BufferDerefCheck>("mpi-buffer-deref"); CheckFactories.registerCheck<TypeMismatchCheck>("mpi-type-mismatch"); } }; diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index eaf2e17..4eb4af8 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -69,6 +69,16 @@ Improvements to clang-tidy Flags classes where some, but not all, special member functions are user-defined. +- New `mpi-buffer-deref + <http://clang.llvm.org/extra/clang-tidy/checks/mpi-buffer-deref.html>`_ check + + Flags buffers which are insufficiently dereferenced when passed to an MPI function call. + +- New `mpi-type-mismatch + <http://clang.llvm.org/extra/clang-tidy/checks/mpi-type-mismatch.html>`_ check + + Flags MPI function calls with a buffer type and MPI data type mismatch. + - New `performance-inefficient-string-concatenation <http://clang.llvm.org/extra/clang-tidy/checks/performance-inefficient-string-concatenation.html>`_ check diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst index 2b90545..9cf98e3 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/list.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -110,6 +110,7 @@ Clang-Tidy Checks modernize-use-nullptr modernize-use-override modernize-use-using + mpi-buffer-deref mpi-type-mismatch performance-faster-string-find performance-for-range-copy diff --git a/clang-tools-extra/docs/clang-tidy/checks/mpi-buffer-deref.rst b/clang-tools-extra/docs/clang-tidy/checks/mpi-buffer-deref.rst new file mode 100644 index 0000000..6a3f426 --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/mpi-buffer-deref.rst @@ -0,0 +1,25 @@ +.. title:: clang-tidy - mpi-buffer-deref + +mpi-buffer-deref +================ + +This check verifies if a buffer passed to an MPI (Message Passing Interface) +function is sufficiently dereferenced. Buffers should be passed as a single +pointer or array. As MPI function signatures specify ``void *`` for their buffer +types, insufficiently dereferenced buffers can be passed, like for example as +double pointers or multidimensional arrays, without a compiler warning emitted. + +Examples: +.. code:: c++ + + // A double pointer is passed to the MPI function. + char *buf; + MPI_Send(&buf, 1, MPI_CHAR, 0, 0, MPI_COMM_WORLD); + + // A multidimensional array is passed to the MPI function. + short buf[1][1]; + MPI_Send(buf, 1, MPI_SHORT, 0, 0, MPI_COMM_WORLD); + + // A pointer to an array is passed to the MPI function. + short *buf[1]; + MPI_Send(buf, 1, MPI_SHORT, 0, 0, MPI_COMM_WORLD); diff --git a/clang-tools-extra/test/clang-tidy/Inputs/mpi-type-mismatch/mpimock.h b/clang-tools-extra/test/clang-tidy/Inputs/mpi-type-mismatch/mpimock.h index 9697b60..cb90028 100644 --- a/clang-tools-extra/test/clang-tidy/Inputs/mpi-type-mismatch/mpimock.h +++ b/clang-tools-extra/test/clang-tidy/Inputs/mpi-type-mismatch/mpimock.h @@ -24,6 +24,7 @@ namespace std { template<class T> struct complex { T real; T imag; }; } #define MPI_DATATYPE_NULL 0 #define MPI_CHAR 0 #define MPI_BYTE 0 +#define MPI_SHORT 0 #define MPI_INT 0 #define MPI_LONG 0 #define MPI_LONG_DOUBLE 0 @@ -31,6 +32,7 @@ namespace std { template<class T> struct complex { T real; T imag; }; } #define MPI_INT8_T 0 #define MPI_UINT8_T 0 #define MPI_UINT16_T 0 +#define MPI_C_FLOAT_COMPLEX 0 #define MPI_C_LONG_DOUBLE_COMPLEX 0 #define MPI_FLOAT 0 #define MPI_DOUBLE 0 diff --git a/clang-tools-extra/test/clang-tidy/mpi-buffer-deref.cpp b/clang-tools-extra/test/clang-tidy/mpi-buffer-deref.cpp new file mode 100644 index 0000000..67304d2 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/mpi-buffer-deref.cpp @@ -0,0 +1,50 @@ +// RUN: %check_clang_tidy %s mpi-buffer-deref %t -- -- -I %S/Inputs/mpi-type-mismatch + +#include "mpimock.h" + +void negativeTests() { + char *buf; + MPI_Send(&buf, 1, MPI_CHAR, 0, 0, MPI_COMM_WORLD); + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer is insufficiently dereferenced: pointer->pointer [mpi-buffer-deref] + + unsigned **buf2; + MPI_Send(buf2, 1, MPI_UNSIGNED, 0, 0, MPI_COMM_WORLD); + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer is insufficiently dereferenced: pointer->pointer + + short buf3[1][1]; + MPI_Send(buf3, 1, MPI_SHORT, 0, 0, MPI_COMM_WORLD); + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer is insufficiently dereferenced: array->array + + long double _Complex *buf4[1]; + MPI_Send(buf4, 1, MPI_C_LONG_DOUBLE_COMPLEX, 0, 0, MPI_COMM_WORLD); + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer is insufficiently dereferenced: pointer->array + + std::complex<float> *buf5[1][1]; + MPI_Send(&buf5, 1, MPI_CXX_FLOAT_COMPLEX, 0, 0, MPI_COMM_WORLD); + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer is insufficiently dereferenced: pointer->array->array->pointer +} + +void positiveTests() { + char buf; + MPI_Send(&buf, 1, MPI_CHAR, 0, 0, MPI_COMM_WORLD); + + unsigned *buf2; + MPI_Send(buf2, 1, MPI_UNSIGNED, 0, 0, MPI_COMM_WORLD); + + short buf3[1][1]; + MPI_Send(buf3[0], 1, MPI_SHORT, 0, 0, MPI_COMM_WORLD); + + long double _Complex *buf4[1]; + MPI_Send(*buf4, 1, MPI_C_LONG_DOUBLE_COMPLEX, 0, 0, MPI_COMM_WORLD); + + long double _Complex buf5[1]; + MPI_Send(buf5, 1, MPI_C_LONG_DOUBLE_COMPLEX, 0, 0, MPI_COMM_WORLD); + + std::complex<float> *buf6[1][1]; + MPI_Send(*buf6[0], 1, MPI_CXX_FLOAT_COMPLEX, 0, 0, MPI_COMM_WORLD); + + // Referencing an array with '&' is valid, as this also points to the + // beginning of the array. + long double _Complex buf7[1]; + MPI_Send(&buf7, 1, MPI_C_LONG_DOUBLE_COMPLEX, 0, 0, MPI_COMM_WORLD); +} |