diff options
author | Alexander Kornienko <alexfh@google.com> | 2016-08-02 20:29:35 +0000 |
---|---|---|
committer | Alexander Kornienko <alexfh@google.com> | 2016-08-02 20:29:35 +0000 |
commit | 5e0a50c2d7eb2ab01c30718b1b9fcb9593cd572b (patch) | |
tree | 30cedc50a1c0a2548a2c150d93aa4843417b06fb /clang-tools-extra/clang-tidy/mpi | |
parent | dc7001afb2d58c56d9c6c6f483fac12a59031f91 (diff) | |
download | bcm5719-llvm-5e0a50c2d7eb2ab01c30718b1b9fcb9593cd572b.tar.gz bcm5719-llvm-5e0a50c2d7eb2ab01c30718b1b9fcb9593cd572b.zip |
[clang-tidy] MPITypeMismatchCheck
This check verifies if buffer type and MPI (Message Passing Interface)
datatype pairs match. All MPI datatypes defined by the MPI standard (3.1)
are verified by this check. User defined typedefs, custom MPI datatypes and
null pointer constants are skipped, in the course of verification.
Instructions on how to apply the check can be found at:
https://github.com/0ax1/MPI-Checker/tree/master/examples
Patch by Alexander Droste!
Differential revision: https://reviews.llvm.org/D21962
llvm-svn: 277516
Diffstat (limited to 'clang-tools-extra/clang-tidy/mpi')
-rw-r--r-- | clang-tools-extra/clang-tidy/mpi/CMakeLists.txt | 15 | ||||
-rw-r--r-- | clang-tools-extra/clang-tidy/mpi/MPITidyModule.cpp | 37 | ||||
-rw-r--r-- | clang-tools-extra/clang-tidy/mpi/TypeMismatchCheck.cpp | 336 | ||||
-rw-r--r-- | clang-tools-extra/clang-tidy/mpi/TypeMismatchCheck.h | 52 |
4 files changed, 440 insertions, 0 deletions
diff --git a/clang-tools-extra/clang-tidy/mpi/CMakeLists.txt b/clang-tools-extra/clang-tidy/mpi/CMakeLists.txt new file mode 100644 index 00000000000..77a52d08241 --- /dev/null +++ b/clang-tools-extra/clang-tidy/mpi/CMakeLists.txt @@ -0,0 +1,15 @@ +set(LLVM_LINK_COMPONENTS support) + +add_clang_library(clangTidyMPIModule + MPITidyModule.cpp + TypeMismatchCheck.cpp + + LINK_LIBS + clangAST + clangASTMatchers + clangBasic + clangLex + clangTidy + clangTidyUtils + clangTooling + ) diff --git a/clang-tools-extra/clang-tidy/mpi/MPITidyModule.cpp b/clang-tools-extra/clang-tidy/mpi/MPITidyModule.cpp new file mode 100644 index 00000000000..babf7b6ad20 --- /dev/null +++ b/clang-tools-extra/clang-tidy/mpi/MPITidyModule.cpp @@ -0,0 +1,37 @@ +//===--- MPITidyModule.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 "../ClangTidy.h" +#include "../ClangTidyModule.h" +#include "../ClangTidyModuleRegistry.h" +#include "TypeMismatchCheck.h" + +namespace clang { +namespace tidy { +namespace mpi { + +class MPIModule : public ClangTidyModule { +public: + void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override { + CheckFactories.registerCheck<TypeMismatchCheck>("mpi-type-mismatch"); + } +}; + +} // namespace mpi + +// Register the MPITidyModule using this statically initialized variable. +static ClangTidyModuleRegistry::Add<mpi::MPIModule> + X("mpi-module", "Adds MPI clang-tidy checks."); + +// This anchor is used to force the linker to link in the generated object file +// and thus register the MPIModule. +volatile int MPIModuleAnchorSource = 0; + +} // namespace tidy +} // namespace clang diff --git a/clang-tools-extra/clang-tidy/mpi/TypeMismatchCheck.cpp b/clang-tools-extra/clang-tidy/mpi/TypeMismatchCheck.cpp new file mode 100644 index 00000000000..30be95c5123 --- /dev/null +++ b/clang-tools-extra/clang-tidy/mpi/TypeMismatchCheck.cpp @@ -0,0 +1,336 @@ +//===--- TypeMismatchCheck.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 "TypeMismatchCheck.h" +#include "clang/Lex/Lexer.h" +#include "clang/StaticAnalyzer/Checkers/MPIFunctionClassifier.h" +#include "clang/Tooling/FixIt.h" +#include <map> +#include <unordered_set> + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace mpi { + +/// Check if a BuiltinType::Kind matches the MPI datatype. +/// +/// \param MultiMap datatype group +/// \param Kind buffer type kind +/// \param MPIDatatype name of the MPI datatype +/// +/// \returns true if the pair matches +static bool +isMPITypeMatching(const std::multimap<BuiltinType::Kind, std::string> &MultiMap, + const BuiltinType::Kind Kind, + const std::string &MPIDatatype) { + auto ItPair = MultiMap.equal_range(Kind); + while (ItPair.first != ItPair.second) { + if (ItPair.first->second == MPIDatatype) + return true; + ++ItPair.first; + } + return false; +} + +/// Check if the MPI datatype is a standard type. +/// +/// \param MPIDatatype name of the MPI datatype +/// +/// \returns true if the type is a standard type +static bool isStandardMPIDatatype(const std::string &MPIDatatype) { + static std::unordered_set<std::string> AllTypes = { + "MPI_C_BOOL", + "MPI_CHAR", + "MPI_SIGNED_CHAR", + "MPI_UNSIGNED_CHAR", + "MPI_WCHAR", + "MPI_INT", + "MPI_LONG", + "MPI_SHORT", + "MPI_LONG_LONG", + "MPI_LONG_LONG_INT", + "MPI_UNSIGNED", + "MPI_UNSIGNED_SHORT", + "MPI_UNSIGNED_LONG", + "MPI_UNSIGNED_LONG_LONG", + "MPI_FLOAT", + "MPI_DOUBLE", + "MPI_LONG_DOUBLE", + "MPI_C_COMPLEX", + "MPI_C_FLOAT_COMPLEX", + "MPI_C_DOUBLE_COMPLEX", + "MPI_C_LONG_DOUBLE_COMPLEX", + "MPI_INT8_T", + "MPI_INT16_T", + "MPI_INT32_T", + "MPI_INT64_T", + "MPI_UINT8_T", + "MPI_UINT16_T", + "MPI_UINT32_T", + "MPI_UINT64_T", + "MPI_CXX_BOOL", + "MPI_CXX_FLOAT_COMPLEX", + "MPI_CXX_DOUBLE_COMPLEX", + "MPI_CXX_LONG_DOUBLE_COMPLEX"}; + + return AllTypes.find(MPIDatatype) != AllTypes.end(); +} + +/// Check if a BuiltinType matches the MPI datatype. +/// +/// \param Builtin the builtin type +/// \param BufferTypeName buffer type name, gets assigned +/// \param MPIDatatype name of the MPI datatype +/// \param LO language options +/// +/// \returns true if the type matches +static bool isBuiltinTypeMatching(const BuiltinType *Builtin, + std::string &BufferTypeName, + const std::string &MPIDatatype, + const LangOptions &LO) { + static std::multimap<BuiltinType::Kind, std::string> BuiltinMatches = { + // On some systems like PPC or ARM, 'char' is unsigned by default which is + // why distinct signedness for the buffer and MPI type is tolerated. + {BuiltinType::SChar, "MPI_CHAR"}, + {BuiltinType::SChar, "MPI_SIGNED_CHAR"}, + {BuiltinType::SChar, "MPI_UNSIGNED_CHAR"}, + {BuiltinType::Char_S, "MPI_CHAR"}, + {BuiltinType::Char_S, "MPI_SIGNED_CHAR"}, + {BuiltinType::Char_S, "MPI_UNSIGNED_CHAR"}, + {BuiltinType::UChar, "MPI_CHAR"}, + {BuiltinType::UChar, "MPI_SIGNED_CHAR"}, + {BuiltinType::UChar, "MPI_UNSIGNED_CHAR"}, + {BuiltinType::Char_U, "MPI_CHAR"}, + {BuiltinType::Char_U, "MPI_SIGNED_CHAR"}, + {BuiltinType::Char_U, "MPI_UNSIGNED_CHAR"}, + {BuiltinType::WChar_S, "MPI_WCHAR"}, + {BuiltinType::WChar_U, "MPI_WCHAR"}, + {BuiltinType::Bool, "MPI_C_BOOL"}, + {BuiltinType::Bool, "MPI_CXX_BOOL"}, + {BuiltinType::Short, "MPI_SHORT"}, + {BuiltinType::Int, "MPI_INT"}, + {BuiltinType::Long, "MPI_LONG"}, + {BuiltinType::LongLong, "MPI_LONG_LONG"}, + {BuiltinType::LongLong, "MPI_LONG_LONG_INT"}, + {BuiltinType::UShort, "MPI_UNSIGNED_SHORT"}, + {BuiltinType::UInt, "MPI_UNSIGNED"}, + {BuiltinType::ULong, "MPI_UNSIGNED_LONG"}, + {BuiltinType::ULongLong, "MPI_UNSIGNED_LONG_LONG"}, + {BuiltinType::Float, "MPI_FLOAT"}, + {BuiltinType::Double, "MPI_DOUBLE"}, + {BuiltinType::LongDouble, "MPI_LONG_DOUBLE"}}; + + if (!isMPITypeMatching(BuiltinMatches, Builtin->getKind(), MPIDatatype)) { + BufferTypeName = Builtin->getName(LO); + return false; + } + + return true; +} + +/// Check if a complex float/double/long double buffer type matches +/// the MPI datatype. +/// +/// \param Complex buffer type +/// \param BufferTypeName buffer type name, gets assigned +/// \param MPIDatatype name of the MPI datatype +/// \param LO language options +/// +/// \returns true if the type matches or the buffer type is unknown +static bool isCComplexTypeMatching(const ComplexType *const Complex, + std::string &BufferTypeName, + const std::string &MPIDatatype, + const LangOptions &LO) { + static std::multimap<BuiltinType::Kind, std::string> ComplexCMatches = { + {BuiltinType::Float, "MPI_C_COMPLEX"}, + {BuiltinType::Float, "MPI_C_FLOAT_COMPLEX"}, + {BuiltinType::Double, "MPI_C_DOUBLE_COMPLEX"}, + {BuiltinType::LongDouble, "MPI_C_LONG_DOUBLE_COMPLEX"}}; + + const auto *Builtin = + Complex->getElementType().getTypePtr()->getAs<BuiltinType>(); + + if (Builtin && + !isMPITypeMatching(ComplexCMatches, Builtin->getKind(), MPIDatatype)) { + BufferTypeName = (llvm::Twine(Builtin->getName(LO)) + " _Complex").str(); + return false; + } + return true; +} + +/// Check if a complex<float/double/long double> templated buffer type matches +/// the MPI datatype. +/// +/// \param Complex buffer type +/// \param BufferTypeName buffer type name, gets assigned +/// \param MPIDatatype name of the MPI datatype +/// \param LO language options +/// +/// \returns true if the type matches or the buffer type is unknown +static bool +isCXXComplexTypeMatching(const TemplateSpecializationType *const Template, + std::string &BufferTypeName, + const std::string &MPIDatatype, + const LangOptions &LO) { + static std::multimap<BuiltinType::Kind, std::string> ComplexCXXMatches = { + {BuiltinType::Float, "MPI_CXX_FLOAT_COMPLEX"}, + {BuiltinType::Double, "MPI_CXX_DOUBLE_COMPLEX"}, + {BuiltinType::LongDouble, "MPI_CXX_LONG_DOUBLE_COMPLEX"}}; + + if (Template->getAsCXXRecordDecl()->getName() != "complex") + return true; + + const auto *Builtin = + Template->getArg(0).getAsType().getTypePtr()->getAs<BuiltinType>(); + + if (Builtin && + !isMPITypeMatching(ComplexCXXMatches, Builtin->getKind(), MPIDatatype)) { + BufferTypeName = + (llvm::Twine("complex<") + Builtin->getName(LO) + ">").str(); + return false; + } + + return true; +} + +/// Check if a fixed size width buffer type matches the MPI datatype. +/// +/// \param Complex buffer type +/// \param BufferTypeName buffer type name, gets assigned +/// \param MPIDatatype name of the MPI datatype +/// +/// \returns true if the type matches or the buffer type is unknown +static bool isTypedefTypeMatching(const TypedefType *const Typedef, + std::string &BufferTypeName, + const std::string &MPIDatatype) { + static llvm::StringMap<std::string> FixedWidthMatches = { + {"int8_t", "MPI_INT8_T"}, {"int16_t", "MPI_INT16_T"}, + {"int32_t", "MPI_INT32_T"}, {"int64_t", "MPI_INT64_T"}, + {"uint8_t", "MPI_UINT8_T"}, {"uint16_t", "MPI_UINT16_T"}, + {"uint32_t", "MPI_UINT32_T"}, {"uint64_t", "MPI_UINT64_T"}}; + + const auto it = FixedWidthMatches.find(Typedef->getDecl()->getName()); + // Check if the typedef is known and not matching the MPI datatype. + if (it != FixedWidthMatches.end() && it->getValue() != MPIDatatype) { + BufferTypeName = Typedef->getDecl()->getName(); + return false; + } + return true; +} + +/// Get the unqualified, dereferenced type of an argument. +/// +/// \param CE call expression +/// \param idx argument index +/// +/// \returns type of the argument +static const Type *argumentType(const CallExpr *const CE, const size_t idx) { + const QualType QT = CE->getArg(idx)->IgnoreImpCasts()->getType(); + return QT.getTypePtr()->getPointeeOrArrayElementType(); +} + +void TypeMismatchCheck::registerMatchers(MatchFinder *Finder) { + Finder->addMatcher(callExpr().bind("CE"), this); +} + +void TypeMismatchCheck::check(const MatchFinder::MatchResult &Result) { + static ento::mpi::MPIFunctionClassifier FuncClassifier(*Result.Context); + const CallExpr *const 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 buffer, MPI datatype pairs. + SmallVector<const Type *, 1> BufferTypes; + SmallVector<const Expr *, 1> BufferExprs; + SmallVector<StringRef, 1> MPIDatatypes; + + // Adds a buffer, MPI datatype pair of an MPI call expression to the + // containers. For buffers, the type and expression is captured. + auto addPair = [&CE, &Result, &BufferTypes, &BufferExprs, &MPIDatatypes]( + const size_t BufferIdx, const size_t DatatypeIdx) { + // 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; + + StringRef MPIDatatype = + tooling::fixit::getText(*CE->getArg(DatatypeIdx), *Result.Context); + + const Type *ArgType = argumentType(CE, BufferIdx); + // Skip unknown MPI datatypes and void pointers. + if (!isStandardMPIDatatype(MPIDatatype) || ArgType->isVoidType()) + return; + + BufferTypes.push_back(ArgType); + BufferExprs.push_back(CE->getArg(BufferIdx)); + MPIDatatypes.push_back(MPIDatatype); + }; + + // Collect all buffer, MPI datatype pairs for the inspected call expression. + if (FuncClassifier.isPointToPointType(Identifier)) { + addPair(0, 2); + } else if (FuncClassifier.isCollectiveType(Identifier)) { + if (FuncClassifier.isReduceType(Identifier)) { + addPair(0, 3); + addPair(1, 3); + } else if (FuncClassifier.isScatterType(Identifier) || + FuncClassifier.isGatherType(Identifier) || + FuncClassifier.isAlltoallType(Identifier)) { + addPair(0, 2); + addPair(3, 5); + } else if (FuncClassifier.isBcastType(Identifier)) { + addPair(0, 2); + } + } + checkArguments(BufferTypes, BufferExprs, MPIDatatypes, + Result.Context->getLangOpts()); +} + +void TypeMismatchCheck::checkArguments(ArrayRef<const Type *> BufferTypes, + ArrayRef<const Expr *> BufferExprs, + ArrayRef<StringRef> MPIDatatypes, + const LangOptions &LO) { + std::string BufferTypeName; + + for (size_t i = 0; i < MPIDatatypes.size(); ++i) { + const Type *const BT = BufferTypes[i]; + bool Error = false; + + if (const auto *Typedef = BT->getAs<TypedefType>()) { + Error = !isTypedefTypeMatching(Typedef, BufferTypeName, MPIDatatypes[i]); + } else if (const auto *Complex = BT->getAs<ComplexType>()) { + Error = + !isCComplexTypeMatching(Complex, BufferTypeName, MPIDatatypes[i], LO); + } else if (const auto *Template = BT->getAs<TemplateSpecializationType>()) { + Error = !isCXXComplexTypeMatching(Template, BufferTypeName, + MPIDatatypes[i], LO); + } else if (const auto *Builtin = BT->getAs<BuiltinType>()) { + Error = + !isBuiltinTypeMatching(Builtin, BufferTypeName, MPIDatatypes[i], LO); + } + + if (Error) { + const auto Loc = BufferExprs[i]->getSourceRange().getBegin(); + diag(Loc, "buffer type '%0' does not match the MPI datatype '%1'") + << BufferTypeName << MPIDatatypes[i]; + } + } +} + +} // namespace mpi +} // namespace tidy +} // namespace clang diff --git a/clang-tools-extra/clang-tidy/mpi/TypeMismatchCheck.h b/clang-tools-extra/clang-tidy/mpi/TypeMismatchCheck.h new file mode 100644 index 00000000000..699110a969c --- /dev/null +++ b/clang-tools-extra/clang-tidy/mpi/TypeMismatchCheck.h @@ -0,0 +1,52 @@ +//===--- TypeMismatchCheck.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_TYPE_MISMATCH_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MPI_TYPE_MISMATCH_H + +#include "../ClangTidy.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +namespace clang { +namespace tidy { +namespace mpi { + +/// This check verifies if buffer type and MPI (Message Passing Interface) +/// datatype pairs match. All MPI datatypes defined by the MPI standard (3.1) +/// are verified by this check. User defined typedefs, custom MPI datatypes and +/// null pointer constants are skipped, in the course of verification. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/mpi-type-mismatch.html +class TypeMismatchCheck : public ClangTidyCheck { +public: + TypeMismatchCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + +private: + /// Check if the buffer type MPI datatype pairs match. + /// + /// \param BufferTypes buffer types + /// \param BufferExprs buffer arguments as expressions + /// \param MPIDatatypes MPI datatype + /// \param LO language options + void checkArguments(ArrayRef<const Type *> BufferTypes, + ArrayRef<const Expr *> BufferExprs, + ArrayRef<StringRef> MPIDatatypes, + const LangOptions &LO); +}; + +} // namespace mpi +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MPI_TYPE_MISMATCH_H |