MPI-Checker patch for Clang Static Analyzer
Alexander Droste via cfe-commits
cfe-commits at lists.llvm.org
Wed Sep 9 05:34:25 PDT 2015
Hi,
this is a patch to add static analysis functionality for MPI code
written in C, in form of a checker to Clang's Static Analyzer. In comparison
to the code currently published on GitHub https://github.com/0ax1/MPI-Checker
I excluded the
unmatched point-to-point call check,
unreachable call check &
collective call in rank branch check
as I do not consider them to be solid enough, to be included
in the patch. These will be rewritten. I'm aware that the
invalid argument type, checking for expressions that evaluate to
a non-integer type, in some way is redundant to the compiler warning
produced by -Wfloat-conversion. My thought about this is that
it might be nice to get informed about this in context of other
MPI related bugs. Further, the -Wfloat-conversion flag seems a little bit
hidden as it is neither included in -Wall nor in -Wextra which is
why some users might not have enabled it by default.
The patch consists of three parts: The actual code for static analysis
contained in tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/,
unit tests in tools/clang/unittests/StaticAnalyzer/MPI-Checker/
and the integration tests I put into tools/clang/test/Analysis/MPIChecker.c.
Best regards,
Alex
-------------- next part --------------
Index: tools/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
===================================================================
--- tools/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt (revision 247122)
+++ tools/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt (working copy)
@@ -45,6 +45,13 @@
MallocChecker.cpp
MallocOverflowSecurityChecker.cpp
MallocSizeofChecker.cpp
+ MPI-Checker/MPIBugReporter.cpp
+ MPI-Checker/MPIChecker.cpp
+ MPI-Checker/MPICheckerAST.cpp
+ MPI-Checker/MPICheckerPathSensitive.cpp
+ MPI-Checker/MPIFunctionClassifier.cpp
+ MPI-Checker/TranslationUnitVisitor.cpp
+ MPI-Checker/Utility.cpp
NSAutoreleasePoolChecker.cpp
NSErrorChecker.cpp
NoReturnFunctionChecker.cpp
Index: tools/clang/lib/StaticAnalyzer/Checkers/Checkers.td
===================================================================
--- tools/clang/lib/StaticAnalyzer/Checkers/Checkers.td (revision 247122)
+++ tools/clang/lib/StaticAnalyzer/Checkers/Checkers.td (working copy)
@@ -27,6 +27,8 @@
def DeadCode : Package<"deadcode">;
def DeadCodeAlpha : Package<"deadcode">, InPackage<Alpha>, Hidden;
+def MPI : Package<"mpi">;
+
def Security : Package <"security">;
def InsecureAPI : Package<"insecureAPI">, InPackage<Security>;
def SecurityAlpha : Package<"security">, InPackage<Alpha>, Hidden;
@@ -516,6 +518,13 @@
DescFile<"ObjCContainersChecker.cpp">;
}
+
+let ParentPackage = MPI in {
+def MPIChecker : Checker<"MPI-Checker">,
+ HelpText<"Checks MPI code written in C">,
+ DescFile<"MPIChecker.cpp">;
+} // end "MPI"
+
//===----------------------------------------------------------------------===//
// Checkers for LLVM development.
//===----------------------------------------------------------------------===//
Index: tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/Container.hpp
===================================================================
--- tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/Container.hpp (revision 0)
+++ tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/Container.hpp (working copy)
@@ -0,0 +1,166 @@
+//===-- Container.hpp - convenience templates for containers ----*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Convenience templates for C++ container classes.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef CONTAINER_HPP_XM1FDRVJ
+#define CONTAINER_HPP_XM1FDRVJ
+
+#include <algorithm>
+
+namespace cont {
+
+/// \brief Check if given element is contained.
+///
+/// \param container
+/// \param elementToCheck
+///
+/// \returns true if element contained
+template <typename T, typename E>
+bool isContained(const T &container, const E &elementToCheck) {
+ return std::find(container.begin(), container.end(), elementToCheck) !=
+ container.end();
+}
+
+/// \brief Check if given element is contained.
+///
+/// \param container
+/// \param elementToCheck
+///
+/// \returns true if element that matched predicate is contained
+template <typename T, typename P>
+bool isContainedPred(const T &container, P predicate) {
+ return std::find_if(container.begin(), container.end(), predicate) !=
+ container.end();
+}
+
+/// \brief Deletes first appearance of given element.
+///
+/// \param container
+/// \param elementToErase
+template <typename T, typename E> void erase(T &container, E &elementToErase) {
+ auto it = std::find(container.begin(), container.end(), elementToErase);
+ if (it != container.end())
+ container.erase(it);
+}
+
+/// \brief Deletes all appearances of given element.
+///
+/// \param container
+/// \param elementToErase
+template <typename T, typename E>
+void eraseAll(T &container, E &elementToErase) {
+ container.erase(
+ std::remove(container.begin(), container.end(), elementToErase),
+ container.end());
+}
+
+/// \brief Deletes first appearance of given pointer.
+///
+/// \param container
+/// \param elementToErase
+template <typename T, typename E>
+void erasePtr(T &container, E elementToErase) {
+ auto it = std::find(container.begin(), container.end(), elementToErase);
+ if (it != container.end())
+ container.erase(it);
+}
+
+/// \brief Deletes first element that matches the predicate.
+///
+/// \param container
+/// \param predicate
+template <typename T, typename P> void erasePred(T &container, P predicate) {
+ auto it = std::find_if(container.begin(), container.end(), predicate);
+ if (it != container.end())
+ container.erase(it);
+}
+
+/// \brief Deletes element at given index.
+///
+/// \param container
+/// \param index
+template <typename T> void eraseIndex(T &container, size_t idx) {
+ container.erase(container.begin() + idx);
+}
+
+/// \brief Sort with default criterion.
+///
+/// \param container
+template <typename T> void sort(T &container) {
+ std::sort(container.begin(), container.end());
+}
+
+/// \brief Sort with given predicate.
+///
+/// \param container
+/// \param predicate
+template <typename T, typename P> void sortPred(T &container, P predicate) {
+ std::sort(container.begin(), container.end(), predicate);
+}
+
+/// \brief Get index for element in container.
+///
+/// \param container
+/// \param element
+/// \returns index of first found element
+template <typename T, typename E>
+size_t index(const T &container, const E &element) {
+ return std::find(container.begin(), container.end(), element) -
+ container.begin();
+}
+
+/// \brief Get index for predicate.
+///
+/// \param container
+/// \param predicate
+/// \returns index of first found element
+template <typename T, typename P>
+size_t indexPred(const T &container, P predicate) {
+ return std::find_if(container.begin(), container.end(), predicate) -
+ container.begin();
+}
+
+/// \brief Copy elements from one container to another.
+///
+/// \param source
+/// \param dest
+template <typename T, typename T2> void copy(const T &source, T2 &dest) {
+ std::copy(source.begin(), source.end(), std::back_inserter(dest));
+}
+
+/// \brief Find an element by predicate. Return an iterator.
+///
+/// \param container
+/// \param predicate
+/// \returns iterator to found element
+template <typename T, typename P>
+typename T::iterator findPred(T &cont, P pred) {
+ return std::find_if(cont.begin(), cont.end(), pred);
+}
+
+/// \brief Checks if two containers are permutations of each other.
+///
+/// \tparam T1 type of container 1
+/// \tparam T2 type of container 2
+/// \param first container
+/// \param second container
+///
+/// \returns isPermutation
+template <typename T1, typename T2>
+bool isPermutation(const T1 &first, const T2 &second) {
+ return std::is_permutation(first.begin(), first.end(), second.begin());
+}
+
+} // end of namespace: cont
+
+#endif // end of include guard: CONTAINER_HPP_XM1FDRVJ
Index: tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.cpp
===================================================================
--- tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.cpp (revision 0)
+++ tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.cpp (working copy)
@@ -0,0 +1,185 @@
+//===-- MPIBugReporter.cpp - bug reporter ----------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Used to emit bug reports for violated invariants detected by AST-based and
+/// path-sensitive analysis.
+///
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "MPIBugReporter.hpp"
+#include "Utility.hpp"
+
+using namespace clang;
+using namespace ento;
+
+namespace mpi {
+
+const std::string MPIError{"MPI Error"};
+const std::string MPIWarning{"MPI Warning"};
+
+std::string
+MPIBugReporter::lineNumber(const CallEventRef<> callEventRef) const {
+ std::string lineNo = callEventRef->getSourceRange().getBegin().printToString(
+ bugReporter_.getSourceManager());
+
+ // split written string into parts
+ std::vector<std::string> strs = util::split(lineNo, ':');
+ return util::split(lineNo, ':').at(strs.size() - 2);
+}
+
+// bug reports ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
+
+// ast reports ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
+void MPIBugReporter::reportTypeMismatch(
+ const CallExpr *callExpr, const std::pair<size_t, size_t> &idxPair,
+ clang::QualType bufferType, std::string mpiType) const {
+ auto adc = analysisManager_.getAnalysisDeclContext(currentFunctionDecl_);
+ PathDiagnosticLocation location = PathDiagnosticLocation::createBegin(
+ callExpr, bugReporter_.getSourceManager(), adc);
+
+ // deref buffer type
+ while (bufferType->isPointerType()) {
+ bufferType = bufferType->getPointeeType();
+ }
+ // remove qualifiers
+ bufferType = bufferType.getUnqualifiedType();
+
+ SourceRange callRange = callExpr->getCallee()->getSourceRange();
+ std::string bugType{"type mismatch"};
+ std::string errorText{"Buffer type '" + bufferType.getAsString() +
+ +"' and specified MPI type '" + mpiType +
+ "' do not match. "};
+
+ llvm::SmallVector<SourceRange, 3> sourceRanges;
+ sourceRanges.push_back(callRange);
+ sourceRanges.push_back(callExpr->getArg(idxPair.first)->getSourceRange());
+ sourceRanges.push_back(callExpr->getArg(idxPair.second)->getSourceRange());
+
+ bugReporter_.EmitBasicReport(adc->getDecl(), &checkerBase_, bugType, MPIError,
+ errorText, location, sourceRanges);
+}
+
+void MPIBugReporter::reportIncorrectBufferReferencing(
+ const CallExpr *callExpr, const std::pair<size_t, size_t> &idxPair,
+ clang::QualType bufferType, size_t pointerCount) const {
+ auto adc = analysisManager_.getAnalysisDeclContext(currentFunctionDecl_);
+ PathDiagnosticLocation location = PathDiagnosticLocation::createBegin(
+ callExpr, bugReporter_.getSourceManager(), adc);
+
+ SourceRange callRange = callExpr->getCallee()->getSourceRange();
+ std::string bugType{"incorrect buffer referencing"};
+ std::string errorText{
+ "Buffer is not correctly dereferenced. It is passed as a " +
+ std::string(pointerCount, '*') + " pointer. "};
+
+ llvm::SmallVector<SourceRange, 2> sourceRanges;
+ sourceRanges.push_back(callRange);
+ sourceRanges.push_back(callExpr->getArg(idxPair.first)->getSourceRange());
+
+ bugReporter_.EmitBasicReport(adc->getDecl(), &checkerBase_, bugType, MPIError,
+ errorText, location, sourceRanges);
+}
+
+void MPIBugReporter::reportInvalidArgumentType(const CallExpr *const callExpr,
+ const size_t idx) const {
+ auto d = analysisManager_.getAnalysisDeclContext(currentFunctionDecl_);
+ PathDiagnosticLocation location = PathDiagnosticLocation::createBegin(
+ callExpr, bugReporter_.getSourceManager(), d);
+
+ std::string indexAsString{std::to_string(idx)};
+ SourceRange callExprRange = callExpr->getCallee()->getSourceRange();
+ std::string bugType{"invalid argument type"};
+ std::string errorText{"The type, argument at index " + indexAsString +
+ " evaluates to, is not an integer type. "};
+
+ SmallVector<SourceRange, 3> sourceRanges;
+ sourceRanges.push_back(callExprRange);
+ sourceRanges.push_back(callExpr->getArg(idx)->getSourceRange());
+ bugReporter_.EmitBasicReport(d->getDecl(), &checkerBase_, bugType, MPIError,
+ errorText, location, sourceRanges);
+}
+
+// path sensitive reports –––––––––––––––––––––––––––––––––––––––––––––––––
+void MPIBugReporter::reportDoubleNonblocking(
+ const CallEvent &observedCall, const Request &request,
+ const ExplodedNode *const node) const {
+ std::string lineNo{lineNumber(request.lastUser_)};
+
+ std::string lastUser = request.lastUser_->getCalleeIdentifier()->getName();
+
+ std::string errorText{"Request '" + request.variableName() +
+ "' is already in use by nonblocking call '" + lastUser +
+ "' in line " + lineNo + ". "};
+
+ auto bugReport =
+ llvm::make_unique<BugReport>(*doubleNonblockingBugType_, errorText, node);
+ bugReport->addRange(observedCall.getSourceRange());
+ bugReport->addRange(request.lastUser_->getSourceRange());
+ SourceRange r = util::sourceRange(request.memRegion_);
+ if (r.isValid())
+ bugReport->addRange(r);
+ bugReporter_.emitReport(std::move(bugReport));
+}
+
+void MPIBugReporter::reportDoubleWait(const CallEvent &observedCall,
+ const Request &request,
+ const ExplodedNode *const node) const {
+ std::string lineNo{lineNumber(request.lastUser_)};
+ std::string lastUser = request.lastUser_->getCalleeIdentifier()->getName();
+ std::string errorText{"Request '" + request.variableName() +
+ "' is already waited upon by '" + lastUser +
+ "' in line " + lineNo + ". "};
+
+ auto bugReport =
+ llvm::make_unique<BugReport>(*doubleWaitBugType_, errorText, node);
+ bugReport->addRange(observedCall.getSourceRange());
+ bugReport->addRange(request.lastUser_->getSourceRange());
+ SourceRange r = util::sourceRange(request.memRegion_);
+ if (r.isValid())
+ bugReport->addRange(r);
+ bugReporter_.emitReport(std::move(bugReport));
+}
+
+void MPIBugReporter::reportMissingWait(const Request &request,
+ const ExplodedNode *const node) const {
+ std::string lineNo{lineNumber(request.lastUser_)};
+ std::string lastUser = request.lastUser_->getCalleeIdentifier()->getName();
+
+ std::string errorText{
+ "'" + lastUser + "' in line " + lineNo + ", using request '" +
+ request.variableName() +
+ "', has no matching wait in the scope of this function. "};
+
+ auto bugReport =
+ llvm::make_unique<BugReport>(*missingWaitBugType_, errorText, node);
+ bugReport->addRange(request.lastUser_->getSourceRange());
+ SourceRange r = util::sourceRange(request.memRegion_);
+ if (r.isValid())
+ bugReport->addRange(r);
+ bugReporter_.emitReport(std::move(bugReport));
+}
+
+void MPIBugReporter::reportUnmatchedWait(
+ const CallEvent &callEvent, const clang::ento::MemRegion *requestRegion,
+ const ExplodedNode *const node) const {
+ std::string errorText{"Request '" + util::variableName(requestRegion) +
+ "' has no matching nonblocking call. "};
+
+ auto bugReport =
+ llvm::make_unique<BugReport>(*unmatchedWaitBugType_, errorText, node);
+ bugReport->addRange(callEvent.getSourceRange());
+ SourceRange r = util::sourceRange(requestRegion);
+ if (r.isValid())
+ bugReport->addRange(r);
+ bugReporter_.emitReport(std::move(bugReport));
+}
+
+} // end of namespace: mpi
Index: tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.hpp
===================================================================
--- tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.hpp (revision 0)
+++ tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.hpp (working copy)
@@ -0,0 +1,123 @@
+//===-- MPIBugReporter.hpp - bug reporter ----------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Emits bug reports for errors detected by AST-based and path-sensitive
+/// analysis.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef MPIBUGREPORTER_HPP_57XZJI4L
+#define MPIBUGREPORTER_HPP_57XZJI4L
+
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "MPITypes.hpp"
+
+namespace mpi {
+
+class MPIBugReporter {
+public:
+ MPIBugReporter(clang::ento::BugReporter &bugReporter,
+ const clang::ento::CheckerBase &checkerBase,
+ clang::ento::AnalysisManager &analysisManager)
+ : bugReporter_{bugReporter}, checkerBase_{checkerBase},
+ analysisManager_{analysisManager} {
+ doubleWaitBugType_.reset(
+ new clang::ento::BugType(&checkerBase, "double wait", "MPI Error"));
+ unmatchedWaitBugType_.reset(
+ new clang::ento::BugType(&checkerBase, "unmatched wait", "MPI Error"));
+ doubleNonblockingBugType_.reset(new clang::ento::BugType(
+ &checkerBase, "double nonblocking", "MPI Error"));
+ missingWaitBugType_.reset(
+ new clang::ento::BugType(&checkerBase, "missing wait", "MPI Error"));
+ }
+
+ // ast reports ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
+
+ /// \brief Reports mismatch between buffer type and MPI datatype.
+ /// \param call expression to report mismatch for
+ /// \param argument indices
+ /// \param buffer type
+ /// \param MPI type
+ void reportTypeMismatch(const clang::CallExpr *const,
+ const std::pair<size_t, size_t> &, clang::QualType,
+ std::string) const;
+
+ /// \brief Report if a buffer is not passed as a single pointer.
+ void reportIncorrectBufferReferencing(const clang::CallExpr *,
+ const std::pair<size_t, size_t> &,
+ clang::QualType,
+ size_t pointerCount) const;
+
+ /// \brief Report non-integer value usage at indices where not allowed.
+ /// (e.g. count, rank)
+ ///
+ /// \param callExpr
+ /// \param idx
+ /// \param type
+ void reportInvalidArgumentType(const clang::CallExpr *const,
+ const size_t) const;
+
+ // path sensitive reports –––––––––––––––––––––––––––––––––––––––––––––––
+
+ /// \brief Report a missing wait for a nonblocking call.
+ ///
+ /// \param request
+ /// \param node
+ void reportMissingWait(const Request &,
+ const clang::ento::ExplodedNode *const) const;
+
+ /// \brief Report there's no matching nonblocking call for request used by
+ /// wait.
+ ///
+ /// \param callExpr
+ /// \param request
+ /// \param node
+ void reportUnmatchedWait(const clang::ento::CallEvent &,
+ const clang::ento::MemRegion *,
+ const clang::ento::ExplodedNode *const) const;
+
+ /// \brief Report duplicate request use by waits.
+ ///
+ /// \param observedCall
+ /// \param request
+ /// \param node
+ void reportDoubleWait(const clang::ento::CallEvent &, const Request &,
+ const clang::ento::ExplodedNode *const) const;
+
+ /// \brief Report duplicate request use by nonblocking calls.
+ ///
+ /// \param observedCall
+ /// \param request
+ /// \param node
+ void reportDoubleNonblocking(const clang::ento::CallEvent &,
+ const mpi::Request &,
+ const clang::ento::ExplodedNode *const) const;
+
+ const clang::Decl *currentFunctionDecl_{nullptr};
+
+private:
+ /// \brief Get line number for call event ref
+ /// \param call event reference
+ /// \returns line number as string
+ std::string lineNumber(const clang::ento::CallEventRef<>) const;
+
+ // path sensitive bug types
+ std::unique_ptr<clang::ento::BugType> unmatchedWaitBugType_;
+ std::unique_ptr<clang::ento::BugType> missingWaitBugType_;
+ std::unique_ptr<clang::ento::BugType> doubleWaitBugType_;
+ std::unique_ptr<clang::ento::BugType> doubleNonblockingBugType_;
+
+ clang::ento::BugReporter &bugReporter_;
+ const clang::ento::CheckerBase &checkerBase_;
+ clang::ento::AnalysisManager &analysisManager_;
+};
+
+} // end of namespace: mpi
+#endif // end of include guard: MPIBUGREPORTER_HPP_57XZJI4L
Index: tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.cpp
===================================================================
--- tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.cpp (revision 0)
+++ tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.cpp (working copy)
@@ -0,0 +1,71 @@
+//===-- MPIChecker.cpp - Checker Entry Point Class -------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Main class which serves as an entry point for this checker.
+/// It is created once for every translation unit.
+///
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "TranslationUnitVisitor.hpp"
+#include "MPICheckerPathSensitive.hpp"
+
+using namespace clang;
+using namespace ento;
+
+namespace mpi {
+class MPIChecker : public Checker<check::ASTDecl<TranslationUnitDecl>,
+ check::PreCall, check::EndFunction> {
+public:
+ // ast callback–––––––––––––––––––––––––––––––––––––––––––––––––––––––
+ void checkASTDecl(const TranslationUnitDecl *tuDecl,
+ AnalysisManager &analysisManager,
+ BugReporter &bugReporter) const {
+
+ // traverse translation unit
+ TranslationUnitVisitor tuVisitor{bugReporter, *this, analysisManager};
+ tuVisitor.TraverseTranslationUnitDecl(
+ const_cast<TranslationUnitDecl *>(tuDecl));
+ }
+
+ // path sensitive callbacks––––––––––––––––––––––––––––––––––––––––––––
+ void checkPreCall(const CallEvent &callEvent, CheckerContext &ctx) const {
+ dynamicInit(ctx);
+ checkerSens_->checkWaitUsage(callEvent, ctx);
+ checkerSens_->checkDoubleNonblocking(callEvent, ctx);
+ }
+
+ void checkEndFunction(CheckerContext &ctx) const {
+ // true if the current LocationContext has no caller context
+ if (ctx.inTopFrame()) {
+ dynamicInit(ctx);
+ checkerSens_->checkMissingWaits(ctx);
+ checkerSens_->clearRequests(ctx);
+ }
+ }
+
+private:
+ const std::unique_ptr<MPICheckerPathSensitive> checkerSens_;
+
+ void dynamicInit(CheckerContext &ctx) const {
+ if (!checkerSens_) {
+ const_cast<std::unique_ptr<MPICheckerPathSensitive> &>(checkerSens_)
+ .reset(new MPICheckerPathSensitive(ctx.getAnalysisManager(), this,
+ ctx.getBugReporter()));
+ }
+ }
+};
+
+} // end of namespace: mpi
+
+// registers the checker for static analysis.
+void ento::registerMPIChecker(CheckerManager &mgr) {
+ mgr.registerChecker<mpi::MPIChecker>();
+}
Index: tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPICheckerAST.cpp
===================================================================
--- tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPICheckerAST.cpp (revision 0)
+++ tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPICheckerAST.cpp (working copy)
@@ -0,0 +1,352 @@
+//===-- MPICheckerAST.cpp - AST-based checks for MPI -----------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// AST-based checks to verify correct usage of the MPI API.
+///
+//===----------------------------------------------------------------------===//
+
+#include "MPICheckerAST.hpp"
+
+using namespace clang;
+using namespace ento;
+
+namespace mpi {
+
+void MPICheckerAST::initMPITypeContainer() {
+ mpiTypes_ = {"MPI_BYTE",
+ "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"};
+}
+
+void MPICheckerAST::checkBufferTypeMatch(
+ const clang::CallExpr *const mpiCall) const {
+ // one pair consists of {bufferIdx, mpiDatatypeIdx}
+ IndexPairs indexPairs = bufferDataTypeIndices(mpiCall);
+
+ // for every buffer mpi-data pair in function
+ // check if their types match
+ for (const auto &idxPair : indexPairs) {
+ auto bufferType =
+ mpiCall->getArg(idxPair.first)->IgnoreImpCasts()->getType();
+
+ // collect buffer type information
+ const mpi::TypeVisitor typeVisitor{bufferType};
+
+ // get MPI datatype as string
+ StringRef mpiDatatypeString{util::sourceRangeAsStringRef(
+ mpiCall->getArg(idxPair.second)->getSourceRange(), analysisManager_)};
+
+ // check if buffer is correctly referenced
+ if (typeVisitor.pointerCount() != 1) {
+ bugReporter_.reportIncorrectBufferReferencing(
+ mpiCall, idxPair, bufferType, typeVisitor.pointerCount());
+ }
+
+ // MPI_BYTE needs no matching
+ if (mpiDatatypeString == "MPI_BYTE")
+ return;
+
+ // if MPI type not known
+ if (!cont::isContained(mpiTypes_, mpiDatatypeString))
+ return;
+
+ selectTypeMatcher(typeVisitor, mpiCall, mpiDatatypeString, idxPair);
+ }
+}
+
+MPICheckerAST::IndexPairs MPICheckerAST::bufferDataTypeIndices(
+ const clang::CallExpr *const mpiCall) const {
+ IndexPairs indexPairs;
+
+ const IdentifierInfo *const ident = util::getIdentInfo(mpiCall);
+
+ if (funcClassifier_.isPointToPointType(ident)) {
+ indexPairs.push_back({MPIPointToPoint::Buf, MPIPointToPoint::Datatype});
+ } else if (funcClassifier_.isCollectiveType(ident)) {
+ if (funcClassifier_.isReduceType(ident)) {
+ // only check buffer type if not inplace
+ if (util::sourceRangeAsStringRef(mpiCall->getArg(0)->getSourceRange(),
+ analysisManager_) != "MPI_IN_PLACE") {
+ indexPairs.push_back({0, 3});
+ }
+ indexPairs.push_back({1, 3});
+ } else if (funcClassifier_.isScatterType(ident) ||
+ funcClassifier_.isGatherType(ident) ||
+ funcClassifier_.isAlltoallType(ident)) {
+ indexPairs.push_back({0, 2});
+ indexPairs.push_back({3, 5});
+ } else if (funcClassifier_.isBcastType(ident)) {
+ indexPairs.push_back({0, 2});
+ }
+ }
+ return indexPairs;
+}
+
+void MPICheckerAST::selectTypeMatcher(
+ const mpi::TypeVisitor &typeVisitor, const clang::CallExpr *const mpiCall,
+ const StringRef mpiDatatypeString,
+ const std::pair<size_t, size_t> &idxPair) const {
+ const clang::BuiltinType *builtinType = typeVisitor.builtinType();
+ bool isTypeMatching{true};
+
+ // check for exact width types (e.g. int16_t, uint32_t)
+ if (typeVisitor.isTypedefType()) {
+ isTypeMatching = matchExactWidthType(typeVisitor, mpiDatatypeString);
+ }
+ // check for complex-floating types (e.g. float _Complex)
+ else if (typeVisitor.isComplexType()) {
+ isTypeMatching = matchComplexType(typeVisitor, mpiDatatypeString);
+ }
+ // check for basic builtin types (e.g. int, char)
+ else if (!builtinType) {
+ return; // if no builtin type cancel checking
+ } else if (builtinType->isBooleanType()) {
+ isTypeMatching = matchBoolType(typeVisitor, mpiDatatypeString);
+ } else if (builtinType->isAnyCharacterType()) {
+ isTypeMatching = matchCharType(typeVisitor, mpiDatatypeString);
+ } else if (builtinType->isSignedInteger()) {
+ isTypeMatching = matchSignedType(typeVisitor, mpiDatatypeString);
+ } else if (builtinType->isUnsignedIntegerType()) {
+ isTypeMatching = matchUnsignedType(typeVisitor, mpiDatatypeString);
+ } else if (builtinType->isFloatingType()) {
+ isTypeMatching = matchFloatType(typeVisitor, mpiDatatypeString);
+ }
+
+ if (!isTypeMatching)
+ bugReporter_.reportTypeMismatch(mpiCall, idxPair, typeVisitor.qualType_,
+ mpiDatatypeString);
+}
+
+bool MPICheckerAST::matchBoolType(const mpi::TypeVisitor &visitor,
+ const llvm::StringRef mpiDatatype) const {
+ return (mpiDatatype == "MPI_C_BOOL");
+}
+
+bool MPICheckerAST::matchCharType(const mpi::TypeVisitor &visitor,
+ const llvm::StringRef mpiDatatype) const {
+ bool isTypeMatching;
+ switch (visitor.builtinType()->getKind()) {
+ case BuiltinType::SChar:
+ isTypeMatching =
+ (mpiDatatype == "MPI_CHAR" || mpiDatatype == "MPI_SIGNED_CHAR");
+ break;
+ case BuiltinType::Char_S:
+ isTypeMatching =
+ (mpiDatatype == "MPI_CHAR" || mpiDatatype == "MPI_SIGNED_CHAR");
+ break;
+ case BuiltinType::UChar:
+ isTypeMatching = (mpiDatatype == "MPI_UNSIGNED_CHAR");
+ break;
+ case BuiltinType::Char_U:
+ isTypeMatching = (mpiDatatype == "MPI_UNSIGNED_CHAR");
+ break;
+ case BuiltinType::WChar_S:
+ isTypeMatching = (mpiDatatype == "MPI_WCHAR");
+ break;
+ case BuiltinType::WChar_U:
+ isTypeMatching = (mpiDatatype == "MPI_WCHAR");
+ break;
+
+ default:
+ isTypeMatching = true;
+ }
+
+ return isTypeMatching;
+}
+
+bool MPICheckerAST::matchSignedType(const mpi::TypeVisitor &visitor,
+ const llvm::StringRef mpiDatatype) const {
+ bool isTypeMatching;
+
+ switch (visitor.builtinType()->getKind()) {
+ case BuiltinType::Int:
+ isTypeMatching = (mpiDatatype == "MPI_INT");
+ break;
+ case BuiltinType::Long:
+ isTypeMatching = (mpiDatatype == "MPI_LONG");
+ break;
+ case BuiltinType::Short:
+ isTypeMatching = (mpiDatatype == "MPI_SHORT");
+ break;
+ case BuiltinType::LongLong:
+ isTypeMatching =
+ (mpiDatatype == "MPI_LONG_LONG" || mpiDatatype == "MPI_LONG_LONG_INT");
+ break;
+ default:
+ isTypeMatching = true;
+ }
+
+ return isTypeMatching;
+}
+
+bool MPICheckerAST::matchUnsignedType(const mpi::TypeVisitor &visitor,
+ const llvm::StringRef mpiDatatype) const {
+ bool isTypeMatching;
+
+ switch (visitor.builtinType()->getKind()) {
+ case BuiltinType::UInt:
+ isTypeMatching = (mpiDatatype == "MPI_UNSIGNED");
+ break;
+ case BuiltinType::UShort:
+ isTypeMatching = (mpiDatatype == "MPI_UNSIGNED_SHORT");
+ break;
+ case BuiltinType::ULong:
+ isTypeMatching = (mpiDatatype == "MPI_UNSIGNED_LONG");
+ break;
+ case BuiltinType::ULongLong:
+ isTypeMatching = (mpiDatatype == "MPI_UNSIGNED_LONG_LONG");
+ break;
+
+ default:
+ isTypeMatching = true;
+ }
+ return isTypeMatching;
+}
+
+bool MPICheckerAST::matchFloatType(const mpi::TypeVisitor &visitor,
+ const llvm::StringRef mpiDatatype) const {
+ bool isTypeMatching;
+
+ switch (visitor.builtinType()->getKind()) {
+ case BuiltinType::Float:
+ isTypeMatching = (mpiDatatype == "MPI_FLOAT");
+ break;
+ case BuiltinType::Double:
+ isTypeMatching = (mpiDatatype == "MPI_DOUBLE");
+ break;
+ case BuiltinType::LongDouble:
+ isTypeMatching = (mpiDatatype == "MPI_LONG_DOUBLE");
+ break;
+ default:
+ isTypeMatching = true;
+ }
+ return isTypeMatching;
+}
+
+bool MPICheckerAST::matchComplexType(const mpi::TypeVisitor &visitor,
+ const llvm::StringRef mpiDatatype) const {
+ bool isTypeMatching;
+
+ switch (visitor.builtinType()->getKind()) {
+ case BuiltinType::Float:
+ isTypeMatching = (mpiDatatype == "MPI_C_COMPLEX" ||
+ mpiDatatype == "MPI_C_FLOAT_COMPLEX");
+ break;
+ case BuiltinType::Double:
+ isTypeMatching = (mpiDatatype == "MPI_C_DOUBLE_COMPLEX");
+ break;
+ case BuiltinType::LongDouble:
+ isTypeMatching = (mpiDatatype == "MPI_C_LONG_DOUBLE_COMPLEX");
+ break;
+ default:
+ isTypeMatching = true;
+ }
+
+ return isTypeMatching;
+}
+
+bool MPICheckerAST::matchExactWidthType(
+ const mpi::TypeVisitor &visitor, const llvm::StringRef mpiDatatype) const {
+ // check typedef type match
+ // no break needs to be specified for string switch
+ bool isTypeMatching = llvm::StringSwitch<bool>(visitor.typedefTypeName())
+ .Case("int8_t", (mpiDatatype == "MPI_INT8_T"))
+ .Case("int16_t", (mpiDatatype == "MPI_INT16_T"))
+ .Case("int32_t", (mpiDatatype == "MPI_INT32_T"))
+ .Case("int64_t", (mpiDatatype == "MPI_INT64_T"))
+
+ .Case("uint8_t", (mpiDatatype == "MPI_UINT8_T"))
+ .Case("uint16_t", (mpiDatatype == "MPI_UINT16_T"))
+ .Case("uint32_t", (mpiDatatype == "MPI_UINT32_T"))
+ .Case("uint64_t", (mpiDatatype == "MPI_UINT64_T"))
+ // unknown typedefs are rated as correct
+ .Default(true);
+
+ return isTypeMatching;
+}
+
+void MPICheckerAST::checkForInvalidArgs(
+ const clang::CallExpr *const mpiCall) const {
+ llvm::SmallVector<size_t, 1> indicesToCheck{integerIndices(mpiCall)};
+ if (!indicesToCheck.size())
+ return;
+
+ // iterate indices which should not have integer arguments
+ for (const size_t idx : indicesToCheck) {
+ if (!mpiCall->getArg(idx)->IgnoreImpCasts()->getType()->isIntegerType()) {
+ bugReporter_.reportInvalidArgumentType(mpiCall, idx);
+ }
+ }
+}
+
+llvm::SmallVector<size_t, 1>
+MPICheckerAST::integerIndices(const clang::CallExpr *const mpiCall) const {
+ llvm::SmallVector<size_t, 1> intIndices;
+
+ const IdentifierInfo *const ident = util::getIdentInfo(mpiCall);
+
+ if (funcClassifier_.isPointToPointType(ident)) {
+ intIndices = {MPIPointToPoint::Count, MPIPointToPoint::Rank,
+ MPIPointToPoint::Tag};
+ } else if (funcClassifier_.isScatterType(ident) ||
+ funcClassifier_.isGatherType(ident)) {
+ if (funcClassifier_.isAllgatherType(ident)) {
+ intIndices = {1, 4};
+ } else {
+ intIndices = {1, 4, 6};
+ }
+ } else if (funcClassifier_.isAlltoallType(ident)) {
+ intIndices = {1, 4};
+ } else if (funcClassifier_.isReduceType(ident)) {
+ if (funcClassifier_.isCollToColl(ident)) {
+ intIndices = {2};
+ } else {
+ intIndices = {2, 5};
+ }
+ } else if (funcClassifier_.isBcastType(ident)) {
+ intIndices = {1, 3};
+ }
+
+ return intIndices;
+}
+
+void MPICheckerAST::setCurrentlyVisitedFunction(
+ const clang::FunctionDecl *const functionDecl) {
+ bugReporter_.currentFunctionDecl_ = functionDecl;
+}
+
+} // end of namespace: mpi
Index: tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPICheckerAST.hpp
===================================================================
--- tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPICheckerAST.hpp (revision 0)
+++ tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPICheckerAST.hpp (working copy)
@@ -0,0 +1,108 @@
+//===-- MPICheckerAST.hpp - AST-based checks for MPI -----------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// AST-based checks to verify correct usage of the MPI API.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef MPICHECKERAST_HPP_O1KSUWZO
+#define MPICHECKERAST_HPP_O1KSUWZO
+
+#include "../ClangSACheckers.h"
+#include "MPIFunctionClassifier.hpp"
+#include "MPIBugReporter.hpp"
+#include "Container.hpp"
+#include "Utility.hpp"
+#include "TypeVisitor.hpp"
+
+namespace mpi {
+
+class MPICheckerAST : public clang::RecursiveASTVisitor<MPICheckerAST> {
+public:
+ MPICheckerAST(clang::ento::BugReporter &bugReporter,
+ const clang::ento::CheckerBase &checkerBase,
+ clang::ento::AnalysisManager &analysisManager)
+ : funcClassifier_{analysisManager},
+ bugReporter_{bugReporter, checkerBase, analysisManager},
+ analysisManager_{analysisManager} {
+ initMPITypeContainer();
+ }
+
+ using IndexPairs = llvm::SmallVector<std::pair<size_t, size_t>, 2>;
+
+ /// \brief Check if invalid argument types are used in a MPI call.
+ ///
+ /// Checks if expressions evaluate to non-integer type at indices where only
+ /// integer values are valid (count, rank, tag).
+ ///
+ /// \param mpiCall to check the arguments for
+ void checkForInvalidArgs(const clang::CallExpr *const) const;
+
+ /// \brief Checks if buffer type and specified MPI datatype matches.
+ /// \param mpiCall call to check type correspondence for
+ void checkBufferTypeMatch(const clang::CallExpr *const) const;
+
+ /// \brief Set function currently visited to pass to bug reporter
+ /// in case of invariant violation.
+ ///
+ /// \param functionDecl current function
+ void setCurrentlyVisitedFunction(const clang::FunctionDecl *const);
+
+ /// \brief Obtain function classifier instance used by MPICheckerAST
+ /// \returns function classifier
+ const MPIFunctionClassifier &funcClassifier() { return funcClassifier_; }
+
+private:
+ /// \brief Init MPI type container to recognize all type tags defined by the
+ /// MPI standard.
+ void initMPITypeContainer();
+
+ /// \brief Returns index pairs for each buffer, datatype pair.
+ ///
+ /// \param mpiCall
+ /// \returns index pairs
+ IndexPairs bufferDataTypeIndices(const clang::CallExpr *const) const;
+
+ /// \brief Return an array of indices that must be integer values for a given
+ /// call.
+ ///
+ /// \param mpiCall
+ ///
+ /// \returns int indices
+ llvm::SmallVector<size_t, 1>
+ integerIndices(const clang::CallExpr *const) const;
+
+ /// \brief Select apprioriate function to match the buffer type against
+ /// the specified MPI datatype.
+ ///
+ /// \param typeVisitor contains information about the buffer
+ /// \param mpiCall call whose arguments are observed
+ /// \param mpiDatatypeString
+ /// \param idxPair (bufferIdx, mpiDatatypeIdx)
+ void selectTypeMatcher(const TypeVisitor &, const clang::CallExpr *const,
+ const clang::StringRef,
+ const std::pair<size_t, size_t> &) const;
+ bool matchBoolType(const TypeVisitor &, const llvm::StringRef) const;
+ bool matchCharType(const TypeVisitor &, const llvm::StringRef) const;
+ bool matchSignedType(const TypeVisitor &, const llvm::StringRef) const;
+ bool matchUnsignedType(const TypeVisitor &, const llvm::StringRef) const;
+ bool matchFloatType(const TypeVisitor &, const llvm::StringRef) const;
+ bool matchComplexType(const TypeVisitor &, const llvm::StringRef) const;
+ bool matchExactWidthType(const TypeVisitor &, const llvm::StringRef) const;
+
+ MPIFunctionClassifier funcClassifier_;
+ llvm::SmallVector<std::string, 32> mpiTypes_;
+ MPIBugReporter bugReporter_;
+ clang::ento::AnalysisManager &analysisManager_;
+};
+
+} // end of namespace: mpi
+
+#endif // end of include guard: MPICHECKERAST_HPP_O1KSUWZO
Index: tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPICheckerPathSensitive.cpp
===================================================================
--- tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPICheckerPathSensitive.cpp (revision 0)
+++ tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPICheckerPathSensitive.cpp (working copy)
@@ -0,0 +1,166 @@
+//===-- MPICheckerPathSensitive.cpp - path-sensitive checks -----*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Path-sensitive checks to verify correct usage of the MPI API.
+///
+//===----------------------------------------------------------------------===//
+
+#include "MPICheckerPathSensitive.hpp"
+#include "Utility.hpp"
+
+namespace mpi {
+
+using namespace clang;
+using namespace ento;
+
+void MPICheckerPathSensitive::checkDoubleNonblocking(
+ const clang::ento::CallEvent &callEvent, CheckerContext &ctx) const {
+ if (!funcClassifier_.isNonBlockingType(callEvent.getCalleeIdentifier())) {
+ return;
+ }
+ const MemRegion *memRegion =
+ callEvent.getArgSVal(callEvent.getNumArgs() - 1).getAsRegion();
+
+ // no way to reason about symbolic region
+ if (memRegion->getBaseRegion()->getAs<SymbolicRegion>())
+ return;
+
+ ProgramStateRef state = ctx.getState();
+ CallEventRef<> callEventRef = callEvent.cloneWithState(state);
+
+ const Request *request = state->get<RequestMap>(memRegion);
+ const ExplodedNode *const node = ctx.addTransition();
+
+ if (request) {
+ if (funcClassifier_.isNonBlockingType(
+ request->lastUser_->getCalleeIdentifier())) {
+ bugReporter_.reportDoubleNonblocking(callEvent, *request, node);
+ }
+ }
+
+ state =
+ state->set<RequestMap>(memRegion, mpi::Request{memRegion, callEventRef});
+ ctx.addTransition(state);
+}
+
+const MemRegion *MPICheckerPathSensitive::memRegionUsedInWait(
+ const clang::ento::CallEvent &callEvent) const {
+ if (funcClassifier_.isMPI_Wait(callEvent.getCalleeIdentifier())) {
+ return callEvent.getArgSVal(0).getAsRegion();
+ } else if (funcClassifier_.isMPI_Waitall(callEvent.getCalleeIdentifier())) {
+ return callEvent.getArgSVal(1).getAsRegion();
+ } else {
+ return (const MemRegion *)nullptr;
+ }
+}
+
+void MPICheckerPathSensitive::collectUsedMemRegions(
+ llvm::SmallVector<const MemRegion *, 2> &requestRegions,
+ const MemRegion *memRegion, const clang::ento::CallEvent &callEvent,
+ CheckerContext &ctx) const {
+ ProgramStateRef state = ctx.getState();
+ MemRegionManager *regionManager = memRegion->getMemRegionManager();
+
+ if (funcClassifier_.isMPI_Waitall(callEvent.getCalleeIdentifier())) {
+ const MemRegion *superRegion{nullptr};
+ if (const ElementRegion *er = memRegion->getAs<ElementRegion>()) {
+ superRegion = er->getSuperRegion();
+ }
+
+ // single request passed to waitall
+ if (!superRegion) {
+ requestRegions.push_back(memRegion);
+ return;
+ }
+
+ auto size = ctx.getStoreManager().getSizeInElements(
+ state, superRegion,
+ callEvent.getArgExpr(1)->getType()->getPointeeType());
+
+ const llvm::APSInt &arrSize = size.getAs<nonloc::ConcreteInt>()->getValue();
+
+ for (size_t i = 0; i < arrSize; ++i) {
+ NonLoc idx = ctx.getSValBuilder().makeArrayIndex(i);
+
+ const ElementRegion *elementRegion = regionManager->getElementRegion(
+ callEvent.getArgExpr(1)->getType()->getPointeeType(), idx,
+ superRegion, ctx.getASTContext());
+
+ requestRegions.push_back(elementRegion->getAs<MemRegion>());
+ }
+ } else if (funcClassifier_.isMPI_Wait(callEvent.getCalleeIdentifier())) {
+ requestRegions.push_back(memRegion);
+ }
+}
+
+void MPICheckerPathSensitive::checkWaitUsage(
+ const clang::ento::CallEvent &callEvent, CheckerContext &ctx) const {
+ if (!funcClassifier_.isWaitType(callEvent.getCalleeIdentifier()))
+ return;
+ const MemRegion *memRegion = memRegionUsedInWait(callEvent);
+ if (!memRegion)
+ return;
+
+ // no way to reason about symbolic region
+ if (memRegion->getBaseRegion()->getAs<SymbolicRegion>())
+ return;
+
+ ProgramStateRef state = ctx.getState();
+ CallEventRef<> callEventRef = callEvent.cloneWithState(state);
+ const ExplodedNode *const node = ctx.addTransition();
+ llvm::SmallVector<const MemRegion *, 2> requestRegions;
+ collectUsedMemRegions(requestRegions, memRegion, callEvent, ctx);
+
+ // check all requestRegions used in wait function
+ for (const auto requestRegion : requestRegions) {
+ const Request *request = state->get<RequestMap>(requestRegion);
+ state =
+ state->set<RequestMap>(requestRegion, {requestRegion, callEventRef});
+ if (request) {
+ // check for double wait
+ if (funcClassifier_.isWaitType(
+ request->lastUser_->getCalleeIdentifier())) {
+ bugReporter_.reportDoubleWait(callEvent, *request, node);
+ }
+ }
+ // no matching nonblocking call
+ else {
+ bugReporter_.reportUnmatchedWait(callEvent, requestRegion, node);
+ }
+ }
+
+ ctx.addTransition(state);
+}
+
+void MPICheckerPathSensitive::checkMissingWaits(CheckerContext &ctx) {
+ ProgramStateRef state = ctx.getState();
+ auto requests = state->get<RequestMap>();
+ ExplodedNode *node = ctx.addTransition();
+ // at the end of a function immediate calls should be matched with wait
+ for (auto &request : requests) {
+ if (request.second.lastUser_ &&
+ funcClassifier_.isNonBlockingType(
+ request.second.lastUser_->getCalleeIdentifier())) {
+ bugReporter_.reportMissingWait(request.second, node);
+ }
+ }
+}
+
+void MPICheckerPathSensitive::clearRequests(CheckerContext &ctx) const {
+ ProgramStateRef state = ctx.getState();
+ auto requests = state->get<RequestMap>();
+ // clear rank container
+ for (auto &request : requests) {
+ state = state->remove<RequestMap>(request.first);
+ }
+ ctx.addTransition(state);
+}
+
+} // end of namespace: mpi
Index: tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPICheckerPathSensitive.hpp
===================================================================
--- tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPICheckerPathSensitive.hpp (revision 0)
+++ tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPICheckerPathSensitive.hpp (working copy)
@@ -0,0 +1,87 @@
+//===-- MPICheckerPathSensitive.hpp - path-sensitive checks -----*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Path-sensitive checks to verify correct usage of the MPI API.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef MPICHECKERPATHSENSITIVE_HPP_BKYOQUPL
+#define MPICHECKERPATHSENSITIVE_HPP_BKYOQUPL
+
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "MPIFunctionClassifier.hpp"
+#include "MPITypes.hpp"
+#include "MPIBugReporter.hpp"
+
+namespace mpi {
+
+class MPICheckerPathSensitive {
+public:
+ MPICheckerPathSensitive(clang::ento::AnalysisManager &analysisManager,
+ const clang::ento::CheckerBase *checkerBase,
+ clang::ento::BugReporter &bugReporter)
+ : funcClassifier_{analysisManager},
+ bugReporter_{bugReporter, *checkerBase, analysisManager} {}
+
+ /// \brief Checks if a request is used by nonblocking calls multiple times
+ /// before intermediate wait.
+ ///
+ /// \param callExpr
+ /// \param ctx
+ void checkDoubleNonblocking(const clang::ento::CallEvent &,
+ clang::ento::CheckerContext &) const;
+ /// \brief Checks if a request is used by wait multiple times without
+ /// intermediate nonblocking call.
+ ///
+ /// \param callExpr
+ /// \param ctx
+ void checkWaitUsage(const clang::ento::CallEvent &,
+ clang::ento::CheckerContext &) const;
+ /// \brief Check if a nonblocking call has no matching wait.
+ ///
+ /// \param ctx
+ void checkMissingWaits(clang::ento::CheckerContext &);
+
+ /// \brief Erase all request vars from the path sensitive map.
+ //
+ /// \param ctx
+ void clearRequests(clang::ento::CheckerContext &) const;
+
+private:
+ /// \brief Returns the memory region used in a wait function.
+ ///
+ /// \param callEvent wait function
+ /// \returns memory region
+ const clang::ento::MemRegion *
+ memRegionUsedInWait(const clang::ento::CallEvent &) const;
+
+ /// \brief Collects all memory regions used in a wait function.
+ ///
+ /// If the wait function uses a single request, this is a single region.
+ /// For wait functions using multiple requests, multiple regions representing
+ /// elements in the array are collected
+ ///
+ /// \param requestRegions vector the regions get pushed into
+ /// \param memRegion top most region to iterate
+ /// \param callEvent function using the region/s
+ /// \param ctx checker context
+ void
+ collectUsedMemRegions(llvm::SmallVector<const clang::ento::MemRegion *, 2> &,
+ const clang::ento::MemRegion *,
+ const clang::ento::CallEvent &,
+ clang::ento::CheckerContext &) const;
+
+ MPIFunctionClassifier funcClassifier_;
+ MPIBugReporter bugReporter_;
+};
+} // end of namespace: mpi
+
+#endif // end of include guard: MPICHECKERPATHSENSITIVE_HPP_BKYOQUPL
Index: tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIFunctionClassifier.cpp
===================================================================
--- tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIFunctionClassifier.cpp (revision 0)
+++ tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIFunctionClassifier.cpp (working copy)
@@ -0,0 +1,362 @@
+//===-- MPIFunctionClassifier.cpp - classifies MPI functions ----*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Identify and classify MPI functions.
+///
+//===----------------------------------------------------------------------===//
+
+#include "MPIFunctionClassifier.hpp"
+#include "Utility.hpp"
+#include "Container.hpp"
+
+using namespace clang;
+using namespace ento;
+
+namespace mpi {
+
+void MPIFunctionClassifier::identifierInit(
+ clang::ento::AnalysisManager &analysisManager) {
+ // init function identifiers
+ initPointToPointIdentifiers(analysisManager);
+ initCollectiveIdentifiers(analysisManager);
+ initAdditionalIdentifiers(analysisManager);
+}
+
+void MPIFunctionClassifier::initPointToPointIdentifiers(
+ clang::ento::AnalysisManager &analysisManager) {
+ ASTContext &context = analysisManager.getASTContext();
+
+ // copy identifiers into the correct classification containers
+ identInfo_MPI_Send_ = &context.Idents.get("MPI_Send");
+ mpiSendTypes_.push_back(identInfo_MPI_Send_);
+ mpiPointToPointTypes_.push_back(identInfo_MPI_Send_);
+ mpiBlockingTypes_.push_back(identInfo_MPI_Send_);
+ mpiType_.push_back(identInfo_MPI_Send_);
+ assert(identInfo_MPI_Send_);
+
+ identInfo_MPI_Isend_ = &context.Idents.get("MPI_Isend");
+ mpiSendTypes_.push_back(identInfo_MPI_Isend_);
+ mpiPointToPointTypes_.push_back(identInfo_MPI_Isend_);
+ mpiNonBlockingTypes_.push_back(identInfo_MPI_Isend_);
+ mpiType_.push_back(identInfo_MPI_Isend_);
+ assert(identInfo_MPI_Isend_);
+
+ identInfo_MPI_Ssend_ = &context.Idents.get("MPI_Ssend");
+ mpiSendTypes_.push_back(identInfo_MPI_Ssend_);
+ mpiPointToPointTypes_.push_back(identInfo_MPI_Ssend_);
+ mpiBlockingTypes_.push_back(identInfo_MPI_Ssend_);
+ mpiType_.push_back(identInfo_MPI_Ssend_);
+ assert(identInfo_MPI_Ssend_);
+
+ identInfo_MPI_Issend_ = &context.Idents.get("MPI_Issend");
+ mpiSendTypes_.push_back(identInfo_MPI_Issend_);
+ mpiPointToPointTypes_.push_back(identInfo_MPI_Issend_);
+ mpiNonBlockingTypes_.push_back(identInfo_MPI_Issend_);
+ mpiType_.push_back(identInfo_MPI_Issend_);
+ assert(identInfo_MPI_Issend_);
+
+ identInfo_MPI_Bsend_ = &context.Idents.get("MPI_Bsend");
+ mpiSendTypes_.push_back(identInfo_MPI_Bsend_);
+ mpiPointToPointTypes_.push_back(identInfo_MPI_Bsend_);
+ mpiBlockingTypes_.push_back(identInfo_MPI_Bsend_);
+ mpiType_.push_back(identInfo_MPI_Bsend_);
+ assert(identInfo_MPI_Bsend_);
+
+ identInfo_MPI_Ibsend_ = &context.Idents.get("MPI_Ibsend");
+ mpiSendTypes_.push_back(identInfo_MPI_Ibsend_);
+ mpiPointToPointTypes_.push_back(identInfo_MPI_Ibsend_);
+ mpiNonBlockingTypes_.push_back(identInfo_MPI_Ibsend_);
+ mpiType_.push_back(identInfo_MPI_Ibsend_);
+ assert(identInfo_MPI_Ibsend_);
+
+ identInfo_MPI_Rsend_ = &context.Idents.get("MPI_Rsend");
+ mpiSendTypes_.push_back(identInfo_MPI_Rsend_);
+ mpiPointToPointTypes_.push_back(identInfo_MPI_Rsend_);
+ mpiBlockingTypes_.push_back(identInfo_MPI_Rsend_);
+ mpiType_.push_back(identInfo_MPI_Rsend_);
+ assert(identInfo_MPI_Rsend_);
+
+ identInfo_MPI_Irsend_ = &context.Idents.get("MPI_Irsend");
+ mpiSendTypes_.push_back(identInfo_MPI_Irsend_);
+ mpiPointToPointTypes_.push_back(identInfo_MPI_Irsend_);
+ mpiBlockingTypes_.push_back(identInfo_MPI_Irsend_);
+ mpiType_.push_back(identInfo_MPI_Irsend_);
+ assert(identInfo_MPI_Irsend_);
+
+ identInfo_MPI_Recv_ = &context.Idents.get("MPI_Recv");
+ mpiRecvTypes_.push_back(identInfo_MPI_Recv_);
+ mpiPointToPointTypes_.push_back(identInfo_MPI_Recv_);
+ mpiBlockingTypes_.push_back(identInfo_MPI_Recv_);
+ mpiType_.push_back(identInfo_MPI_Recv_);
+ assert(identInfo_MPI_Recv_);
+
+ identInfo_MPI_Irecv_ = &context.Idents.get("MPI_Irecv");
+ mpiRecvTypes_.push_back(identInfo_MPI_Irecv_);
+ mpiPointToPointTypes_.push_back(identInfo_MPI_Irecv_);
+ mpiNonBlockingTypes_.push_back(identInfo_MPI_Irecv_);
+ mpiType_.push_back(identInfo_MPI_Irecv_);
+ assert(identInfo_MPI_Irecv_);
+}
+
+void MPIFunctionClassifier::initCollectiveIdentifiers(
+ clang::ento::AnalysisManager &analysisManager) {
+ ASTContext &context = analysisManager.getASTContext();
+
+ // copy identifiers into the correct classification containers
+ identInfo_MPI_Scatter_ = &context.Idents.get("MPI_Scatter");
+ mpiCollectiveTypes_.push_back(identInfo_MPI_Scatter_);
+ mpiPointToCollTypes_.push_back(identInfo_MPI_Scatter_);
+ mpiBlockingTypes_.push_back(identInfo_MPI_Scatter_);
+ mpiType_.push_back(identInfo_MPI_Scatter_);
+ assert(identInfo_MPI_Scatter_);
+
+ identInfo_MPI_Iscatter_ = &context.Idents.get("MPI_Iscatter");
+ mpiCollectiveTypes_.push_back(identInfo_MPI_Iscatter_);
+ mpiPointToCollTypes_.push_back(identInfo_MPI_Iscatter_);
+ mpiNonBlockingTypes_.push_back(identInfo_MPI_Iscatter_);
+ mpiType_.push_back(identInfo_MPI_Iscatter_);
+ assert(identInfo_MPI_Iscatter_);
+
+ identInfo_MPI_Gather_ = &context.Idents.get("MPI_Gather");
+ mpiCollectiveTypes_.push_back(identInfo_MPI_Gather_);
+ mpiCollToPointTypes_.push_back(identInfo_MPI_Gather_);
+ mpiBlockingTypes_.push_back(identInfo_MPI_Gather_);
+ mpiType_.push_back(identInfo_MPI_Gather_);
+ assert(identInfo_MPI_Gather_);
+
+ identInfo_MPI_Igather_ = &context.Idents.get("MPI_Igather");
+ mpiCollectiveTypes_.push_back(identInfo_MPI_Igather_);
+ mpiCollToPointTypes_.push_back(identInfo_MPI_Igather_);
+ mpiNonBlockingTypes_.push_back(identInfo_MPI_Igather_);
+ mpiType_.push_back(identInfo_MPI_Igather_);
+ assert(identInfo_MPI_Igather_);
+
+ identInfo_MPI_Allgather_ = &context.Idents.get("MPI_Allgather");
+ mpiCollectiveTypes_.push_back(identInfo_MPI_Allgather_);
+ mpiCollToCollTypes_.push_back(identInfo_MPI_Allgather_);
+ mpiBlockingTypes_.push_back(identInfo_MPI_Allgather_);
+ mpiType_.push_back(identInfo_MPI_Allgather_);
+ assert(identInfo_MPI_Allgather_);
+
+ identInfo_MPI_Iallgather_ = &context.Idents.get("MPI_Iallgather");
+ mpiCollectiveTypes_.push_back(identInfo_MPI_Iallgather_);
+ mpiCollToCollTypes_.push_back(identInfo_MPI_Iallgather_);
+ mpiNonBlockingTypes_.push_back(identInfo_MPI_Iallgather_);
+ mpiType_.push_back(identInfo_MPI_Iallgather_);
+ assert(identInfo_MPI_Iallgather_);
+
+ identInfo_MPI_Bcast_ = &context.Idents.get("MPI_Bcast");
+ mpiCollectiveTypes_.push_back(identInfo_MPI_Bcast_);
+ mpiPointToCollTypes_.push_back(identInfo_MPI_Bcast_);
+ mpiBlockingTypes_.push_back(identInfo_MPI_Bcast_);
+ mpiType_.push_back(identInfo_MPI_Bcast_);
+ assert(identInfo_MPI_Bcast_);
+
+ identInfo_MPI_Ibcast_ = &context.Idents.get("MPI_Ibcast");
+ mpiCollectiveTypes_.push_back(identInfo_MPI_Ibcast_);
+ mpiPointToCollTypes_.push_back(identInfo_MPI_Ibcast_);
+ mpiNonBlockingTypes_.push_back(identInfo_MPI_Ibcast_);
+ mpiType_.push_back(identInfo_MPI_Ibcast_);
+ assert(identInfo_MPI_Ibcast_);
+
+ identInfo_MPI_Reduce_ = &context.Idents.get("MPI_Reduce");
+ mpiCollectiveTypes_.push_back(identInfo_MPI_Reduce_);
+ mpiCollToPointTypes_.push_back(identInfo_MPI_Reduce_);
+ mpiBlockingTypes_.push_back(identInfo_MPI_Reduce_);
+ mpiType_.push_back(identInfo_MPI_Reduce_);
+ assert(identInfo_MPI_Reduce_);
+
+ identInfo_MPI_Ireduce_ = &context.Idents.get("MPI_Ireduce");
+ mpiCollectiveTypes_.push_back(identInfo_MPI_Ireduce_);
+ mpiCollToPointTypes_.push_back(identInfo_MPI_Ireduce_);
+ mpiNonBlockingTypes_.push_back(identInfo_MPI_Ireduce_);
+ mpiType_.push_back(identInfo_MPI_Ireduce_);
+ assert(identInfo_MPI_Ireduce_);
+
+ identInfo_MPI_Allreduce_ = &context.Idents.get("MPI_Allreduce");
+ mpiCollectiveTypes_.push_back(identInfo_MPI_Allreduce_);
+ mpiCollToCollTypes_.push_back(identInfo_MPI_Allreduce_);
+ mpiBlockingTypes_.push_back(identInfo_MPI_Allreduce_);
+ mpiType_.push_back(identInfo_MPI_Allreduce_);
+ assert(identInfo_MPI_Allreduce_);
+
+ identInfo_MPI_Iallreduce_ = &context.Idents.get("MPI_Iallreduce");
+ mpiCollectiveTypes_.push_back(identInfo_MPI_Iallreduce_);
+ mpiCollToCollTypes_.push_back(identInfo_MPI_Iallreduce_);
+ mpiNonBlockingTypes_.push_back(identInfo_MPI_Iallreduce_);
+ mpiType_.push_back(identInfo_MPI_Iallreduce_);
+ assert(identInfo_MPI_Iallreduce_);
+
+ identInfo_MPI_Alltoall_ = &context.Idents.get("MPI_Alltoall");
+ mpiCollectiveTypes_.push_back(identInfo_MPI_Alltoall_);
+ mpiCollToCollTypes_.push_back(identInfo_MPI_Alltoall_);
+ mpiBlockingTypes_.push_back(identInfo_MPI_Alltoall_);
+ mpiType_.push_back(identInfo_MPI_Alltoall_);
+ assert(identInfo_MPI_Alltoall_);
+
+ identInfo_MPI_Ialltoall_ = &context.Idents.get("MPI_Ialltoall");
+ mpiCollectiveTypes_.push_back(identInfo_MPI_Ialltoall_);
+ mpiCollToCollTypes_.push_back(identInfo_MPI_Ialltoall_);
+ mpiNonBlockingTypes_.push_back(identInfo_MPI_Ialltoall_);
+ mpiType_.push_back(identInfo_MPI_Ialltoall_);
+ assert(identInfo_MPI_Ialltoall_);
+}
+
+void MPIFunctionClassifier::initAdditionalIdentifiers(
+ clang::ento::AnalysisManager &analysisManager) {
+ ASTContext &context = analysisManager.getASTContext();
+
+ identInfo_MPI_Comm_rank_ = &context.Idents.get("MPI_Comm_rank");
+ mpiType_.push_back(identInfo_MPI_Comm_rank_);
+ assert(identInfo_MPI_Comm_rank_);
+
+ identInfo_MPI_Comm_size_ = &context.Idents.get("MPI_Comm_size");
+ mpiType_.push_back(identInfo_MPI_Comm_size_);
+ assert(identInfo_MPI_Comm_size_);
+
+ identInfo_MPI_Wait_ = &context.Idents.get("MPI_Wait");
+ mpiType_.push_back(identInfo_MPI_Wait_);
+ assert(identInfo_MPI_Wait_);
+
+ identInfo_MPI_Waitall_ = &context.Idents.get("MPI_Waitall");
+ mpiType_.push_back(identInfo_MPI_Waitall_);
+ assert(identInfo_MPI_Waitall_);
+
+ identInfo_MPI_Waitany_ = &context.Idents.get("MPI_Waitany");
+ mpiType_.push_back(identInfo_MPI_Waitany_);
+ assert(identInfo_MPI_Waitany_);
+
+ identInfo_MPI_Waitsome_ = &context.Idents.get("MPI_Waitsome");
+ mpiType_.push_back(identInfo_MPI_Waitsome_);
+ assert(identInfo_MPI_Waitsome_);
+
+ identInfo_MPI_Barrier_ = &context.Idents.get("MPI_Barrier");
+ mpiCollectiveTypes_.push_back(identInfo_MPI_Barrier_);
+ mpiType_.push_back(identInfo_MPI_Barrier_);
+ assert(identInfo_MPI_Barrier_);
+}
+
+// general identifiers–––––––––––––––––––––––––––––––––––––––––––––––––
+bool MPIFunctionClassifier::isMPIType(const IdentifierInfo *identInfo) const {
+ return cont::isContained(mpiType_, identInfo);
+}
+
+bool MPIFunctionClassifier::isBlockingType(
+ const IdentifierInfo *identInfo) const {
+ return cont::isContained(mpiBlockingTypes_, identInfo);
+}
+
+bool MPIFunctionClassifier::isNonBlockingType(
+ const IdentifierInfo *identInfo) const {
+ return cont::isContained(mpiNonBlockingTypes_, identInfo);
+}
+
+// point to point identifiers––––––––––––––––––––––––––––––––––––––––––
+bool MPIFunctionClassifier::isPointToPointType(
+ const IdentifierInfo *identInfo) const {
+ return cont::isContained(mpiPointToPointTypes_, identInfo);
+}
+
+bool MPIFunctionClassifier::isSendType(const IdentifierInfo *identInfo) const {
+ return cont::isContained(mpiSendTypes_, identInfo);
+}
+
+bool MPIFunctionClassifier::isRecvType(const IdentifierInfo *identInfo) const {
+ return cont::isContained(mpiRecvTypes_, identInfo);
+}
+
+// collective identifiers––––––––––––––––––––––––––––––––––––––––––––––
+bool MPIFunctionClassifier::isCollectiveType(
+ const IdentifierInfo *identInfo) const {
+ return cont::isContained(mpiCollectiveTypes_, identInfo);
+}
+
+bool MPIFunctionClassifier::isCollToColl(
+ const IdentifierInfo *identInfo) const {
+ return cont::isContained(mpiCollToCollTypes_, identInfo);
+}
+
+bool MPIFunctionClassifier::isScatterType(
+ const IdentifierInfo *identInfo) const {
+ return identInfo == identInfo_MPI_Scatter_ ||
+ identInfo == identInfo_MPI_Iscatter_;
+}
+
+bool MPIFunctionClassifier::isGatherType(
+ const IdentifierInfo *identInfo) const {
+ return identInfo == identInfo_MPI_Gather_ ||
+ identInfo == identInfo_MPI_Igather_ ||
+ identInfo == identInfo_MPI_Allgather_ ||
+ identInfo == identInfo_MPI_Iallgather_;
+}
+
+bool MPIFunctionClassifier::isAllgatherType(
+ const IdentifierInfo *identInfo) const {
+ return identInfo == identInfo_MPI_Allgather_ ||
+ identInfo == identInfo_MPI_Iallgather_;
+}
+
+bool MPIFunctionClassifier::isAlltoallType(
+ const IdentifierInfo *identInfo) const {
+ return identInfo == identInfo_MPI_Alltoall_ ||
+ identInfo == identInfo_MPI_Ialltoall_;
+}
+
+bool MPIFunctionClassifier::isBcastType(const IdentifierInfo *identInfo) const {
+ return identInfo == identInfo_MPI_Bcast_ ||
+ identInfo == identInfo_MPI_Ibcast_;
+}
+
+bool MPIFunctionClassifier::isReduceType(
+ const IdentifierInfo *identInfo) const {
+ return identInfo == identInfo_MPI_Reduce_ ||
+ identInfo == identInfo_MPI_Ireduce_ ||
+ identInfo == identInfo_MPI_Allreduce_ ||
+ identInfo == identInfo_MPI_Iallreduce_;
+}
+
+// additional identifiers ––––––––––––––––––––––––––––––––––––––––––––––
+bool MPIFunctionClassifier::isMPI_Comm_rank(
+ const IdentifierInfo *identInfo) const {
+ return identInfo == identInfo_MPI_Comm_rank_;
+}
+
+bool MPIFunctionClassifier::isMPI_Comm_size(
+ const IdentifierInfo *identInfo) const {
+ return identInfo == identInfo_MPI_Comm_size_;
+}
+
+bool MPIFunctionClassifier::isMPI_Wait(const IdentifierInfo *identInfo) const {
+ return identInfo == identInfo_MPI_Wait_;
+}
+
+bool MPIFunctionClassifier::isMPI_Waitall(
+ const IdentifierInfo *identInfo) const {
+ return identInfo == identInfo_MPI_Waitall_;
+}
+
+bool MPIFunctionClassifier::isMPI_Waitany(
+ const IdentifierInfo *identInfo) const {
+ return identInfo == identInfo_MPI_Waitany_;
+}
+
+bool MPIFunctionClassifier::isMPI_Waitsome(
+ const IdentifierInfo *identInfo) const {
+ return identInfo == identInfo_MPI_Waitsome_;
+}
+
+bool MPIFunctionClassifier::isWaitType(const IdentifierInfo *identInfo) const {
+ return identInfo == identInfo_MPI_Wait_ ||
+ identInfo == identInfo_MPI_Waitall_ ||
+ identInfo == identInfo_MPI_Waitany_ ||
+ identInfo == identInfo_MPI_Waitsome_;
+}
+
+} // end of namespace: mpi
Index: tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIFunctionClassifier.hpp
===================================================================
--- tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIFunctionClassifier.hpp (revision 0)
+++ tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIFunctionClassifier.hpp (working copy)
@@ -0,0 +1,113 @@
+//===-- MPIFunctionClassifier.hpp - classifies MPI functions ----*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Identify and classify MPI functions.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef MPIFUNCTIONCLASSIFIER_HPP_Q3AOUNFC
+#define MPIFUNCTIONCLASSIFIER_HPP_Q3AOUNFC
+
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+
+namespace mpi {
+
+class MPIFunctionClassifier {
+public:
+ MPIFunctionClassifier(clang::ento::AnalysisManager &analysisManager) {
+ identifierInit(analysisManager);
+ }
+
+ // general identifiers–––––––––––––––––––––––––––––––––––––––––––––––––
+ bool isMPIType(const clang::IdentifierInfo *const) const;
+ bool isBlockingType(const clang::IdentifierInfo *const) const;
+ bool isNonBlockingType(const clang::IdentifierInfo *const) const;
+
+ // point to point identifiers––––––––––––––––––––––––––––––––––––––––––
+ bool isPointToPointType(const clang::IdentifierInfo *const) const;
+ bool isSendType(const clang::IdentifierInfo *const) const;
+ bool isRecvType(const clang::IdentifierInfo *const) const;
+
+ // collective identifiers––––––––––––––––––––––––––––––––––––––––––––––
+ bool isCollectiveType(const clang::IdentifierInfo *const) const;
+ bool isCollToColl(const clang::IdentifierInfo *const) const;
+ bool isScatterType(const clang::IdentifierInfo *const) const;
+ bool isGatherType(const clang::IdentifierInfo *const) const;
+ bool isAllgatherType(const clang::IdentifierInfo *const) const;
+ bool isAlltoallType(const clang::IdentifierInfo *const) const;
+ bool isReduceType(const clang::IdentifierInfo *const) const;
+ bool isBcastType(const clang::IdentifierInfo *const) const;
+
+ // additional identifiers ––––––––––––––––––––––––––––––––––––––––––––––
+ bool isMPI_Comm_rank(const clang::IdentifierInfo *const) const;
+ bool isMPI_Comm_size(const clang::IdentifierInfo *const) const;
+ bool isMPI_Wait(const clang::IdentifierInfo *const) const;
+ bool isMPI_Waitall(const clang::IdentifierInfo *const) const;
+ bool isMPI_Waitany(const clang::IdentifierInfo *const) const;
+ bool isMPI_Waitsome(const clang::IdentifierInfo *const) const;
+ bool isWaitType(const clang::IdentifierInfo *const) const;
+
+private:
+ /// \brief Initializes function identifiers.
+ ///
+ /// Initializes function identifiers. Instead of using strings,
+ /// indentifier-pointers are initially captured
+ /// to recognize functions during analysis by comparison later.
+ //
+ /// \param analysis manager
+ void identifierInit(clang::ento::AnalysisManager &);
+ void initPointToPointIdentifiers(clang::ento::AnalysisManager &);
+ void initCollectiveIdentifiers(clang::ento::AnalysisManager &);
+ void initAdditionalIdentifiers(clang::ento::AnalysisManager &);
+
+ // to enable classification of mpi-functions during analysis
+ llvm::SmallVector<clang::IdentifierInfo *, 8> mpiSendTypes_;
+ llvm::SmallVector<clang::IdentifierInfo *, 2> mpiRecvTypes_;
+
+ llvm::SmallVector<clang::IdentifierInfo *, 12> mpiBlockingTypes_;
+ llvm::SmallVector<clang::IdentifierInfo *, 12> mpiNonBlockingTypes_;
+
+ llvm::SmallVector<clang::IdentifierInfo *, 10> mpiPointToPointTypes_;
+ llvm::SmallVector<clang::IdentifierInfo *, 16> mpiCollectiveTypes_;
+
+ llvm::SmallVector<clang::IdentifierInfo *, 4> mpiPointToCollTypes_;
+ llvm::SmallVector<clang::IdentifierInfo *, 4> mpiCollToPointTypes_;
+ llvm::SmallVector<clang::IdentifierInfo *, 6> mpiCollToCollTypes_;
+
+ llvm::SmallVector<clang::IdentifierInfo *, 32> mpiType_;
+
+ // point to point functions
+ clang::IdentifierInfo *identInfo_MPI_Send_{nullptr},
+ *identInfo_MPI_Isend_{nullptr}, *identInfo_MPI_Ssend_{nullptr},
+ *identInfo_MPI_Issend_{nullptr}, *identInfo_MPI_Bsend_{nullptr},
+ *identInfo_MPI_Ibsend_{nullptr}, *identInfo_MPI_Rsend_{nullptr},
+ *identInfo_MPI_Irsend_{nullptr}, *identInfo_MPI_Recv_{nullptr},
+ *identInfo_MPI_Irecv_{nullptr};
+
+ // collective functions
+ clang::IdentifierInfo *identInfo_MPI_Scatter_{nullptr},
+ *identInfo_MPI_Iscatter_{nullptr}, *identInfo_MPI_Gather_{nullptr},
+ *identInfo_MPI_Igather_{nullptr}, *identInfo_MPI_Allgather_{nullptr},
+ *identInfo_MPI_Iallgather_{nullptr}, *identInfo_MPI_Bcast_{nullptr},
+ *identInfo_MPI_Ibcast_{nullptr}, *identInfo_MPI_Reduce_{nullptr},
+ *identInfo_MPI_Ireduce_{nullptr}, *identInfo_MPI_Allreduce_{nullptr},
+ *identInfo_MPI_Iallreduce_{nullptr}, *identInfo_MPI_Alltoall_{nullptr},
+ *identInfo_MPI_Ialltoall_{nullptr}, *identInfo_MPI_Barrier_{nullptr};
+
+ // additional functions
+ clang::IdentifierInfo *identInfo_MPI_Comm_rank_{nullptr},
+ *identInfo_MPI_Comm_size_{nullptr}, *identInfo_MPI_Wait_{nullptr},
+ *identInfo_MPI_Waitall_{nullptr}, *identInfo_MPI_Waitany_{nullptr},
+ *identInfo_MPI_Waitsome_{nullptr};
+};
+
+} // end of namespace: mpi
+
+#endif // end of include guard: MPIFUNCTIONCLASSIFIER_HPP_Q3AOUNFC
Index: tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPITypes.hpp
===================================================================
--- tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPITypes.hpp (revision 0)
+++ tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPITypes.hpp (working copy)
@@ -0,0 +1,66 @@
+//===-- MPITypes.h - Functionality to model MPI concepts --------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Definitions to model MPI point-to-point schema, MPI request.
+///
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef MPITYPES_HPP_IC7XR2MI
+#define MPITYPES_HPP_IC7XR2MI
+
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "llvm/ADT/SmallSet.h"
+#include "MPIFunctionClassifier.hpp"
+#include "Utility.hpp"
+#include "Container.hpp"
+
+namespace mpi {
+// argument schema enums –––––––––––––––––––––––––––––––––––––––––––––––
+// scope enums, but keep weak typing to make values usable as indices
+namespace MPIPointToPoint {
+// valid for all point to point functions
+enum { Buf, Count, Datatype, Rank, Tag, Comm, Request };
+}
+
+// for path sensitive analysis–––––––––––––––––––––––––––––––––––––––––––––––
+class Request {
+
+public:
+ Request(const clang::ento::MemRegion *const memRegion,
+ const clang::ento::CallEventRef<> callEvent)
+ : memRegion_{memRegion}, lastUser_{callEvent} {
+ variableName_ = util::variableName(memRegion);
+ }
+
+ void Profile(llvm::FoldingSetNodeID &id) const {
+ id.AddPointer(memRegion_);
+ id.AddPointer(lastUser_->getOriginExpr());
+ }
+
+ bool operator==(const Request &toCompare) const {
+ return toCompare.memRegion_ == memRegion_;
+ }
+
+ const clang::ento::MemRegion *const memRegion_;
+ const clang::ento::CallEventRef<> lastUser_;
+
+ std::string variableName() const { return variableName_; }
+
+private:
+ std::string variableName_;
+};
+} // end of namespace: mpi
+
+// register data structure for path sensitive analysis
+REGISTER_MAP_WITH_PROGRAMSTATE(RequestMap, const clang::ento::MemRegion *,
+ mpi::Request)
+
+#endif // end of include guard: MPITYPES_HPP_IC7XR2MI
Index: tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/TranslationUnitVisitor.cpp
===================================================================
--- tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/TranslationUnitVisitor.cpp (revision 0)
+++ tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/TranslationUnitVisitor.cpp (working copy)
@@ -0,0 +1,42 @@
+//===-- TranslationUnitVisitor.cpp - traverses tu --*-------------- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// AST-based checks are invoked on an MPICheckerAST instance during traversal
+/// by the TranslationUnitVisitor.
+///
+//===----------------------------------------------------------------------===//
+
+#include "TranslationUnitVisitor.hpp"
+#include "MPICheckerPathSensitive.hpp"
+
+using namespace clang;
+using namespace ento;
+
+namespace mpi {
+
+bool TranslationUnitVisitor::VisitFunctionDecl(FunctionDecl *functionDecl) {
+ // to keep track which function implementation is currently analysed
+ if (functionDecl->clang::Decl::hasBody() && !functionDecl->isInlined()) {
+ // to make display of function in diagnostics available
+ checkerAST_.setCurrentlyVisitedFunction(functionDecl);
+ }
+ return true;
+}
+
+bool TranslationUnitVisitor::VisitCallExpr(clang::CallExpr *mpiCall) {
+ if (checkerAST_.funcClassifier().isMPIType(util::getIdentInfo(mpiCall))) {
+ checkerAST_.checkBufferTypeMatch(mpiCall);
+ checkerAST_.checkForInvalidArgs(mpiCall);
+ }
+
+ return true;
+}
+
+} // end of namespace: mpi
Index: tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/TranslationUnitVisitor.hpp
===================================================================
--- tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/TranslationUnitVisitor.hpp (revision 0)
+++ tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/TranslationUnitVisitor.hpp (working copy)
@@ -0,0 +1,52 @@
+//===-- TranslationUnitVisitor.hpp - traverses tu --*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// AST-based checks are invoked on an MPICheckerAST instance during traversal
+/// by the TranslationUnitVisitor.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef MPISCHEMACHECKERAST_HPP_NKN9I06D
+#define MPISCHEMACHECKERAST_HPP_NKN9I06D
+
+#include "MPICheckerAST.hpp"
+
+namespace mpi {
+
+class TranslationUnitVisitor
+ : public clang::RecursiveASTVisitor<TranslationUnitVisitor> {
+public:
+ TranslationUnitVisitor(clang::ento::BugReporter &bugReporter,
+ const clang::ento::CheckerBase &checkerBase,
+ clang::ento::AnalysisManager &analysisManager)
+ : checkerAST_{bugReporter, checkerBase, analysisManager} {}
+
+ // visitor callbacks----------------------------
+
+ /// \brief Visited for each function declaration.
+ ///
+ /// \param functionDecl
+ ///
+ /// \returns continue visiting
+ bool VisitFunctionDecl(clang::FunctionDecl *);
+
+ /// \brief Visited for each function call.
+ ///
+ /// \param callExpr
+ ///
+ /// \returns continue visiting
+ bool VisitCallExpr(clang::CallExpr *);
+
+ MPICheckerAST checkerAST_;
+};
+
+} // end of namespace: mpi
+
+#endif // end of include guard: MPISCHEMACHECKERAST_HPP_NKN9I06D
Index: tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/TypeVisitor.hpp
===================================================================
--- tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/TypeVisitor.hpp (revision 0)
+++ tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/TypeVisitor.hpp (working copy)
@@ -0,0 +1,75 @@
+//===-- TypeVisitor - traverse qual type -----------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Traverses qualified type to collect information about it.
+/// Detects if a QualType is a typedef, complex, builtin type.
+/// For matches the type information is stored.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef TYPEVISITOR_HPP_KOZYUVZH
+#define TYPEVISITOR_HPP_KOZYUVZH
+
+#include "clang/AST/RecursiveASTVisitor.h"
+
+namespace mpi {
+
+class TypeVisitor : public clang::RecursiveASTVisitor<TypeVisitor> {
+public:
+ TypeVisitor(clang::QualType qualType) : qualType_{qualType} {
+ TraverseType(qualType);
+ }
+
+ bool VisitTypedefType(clang::TypedefType *tdt) {
+ typedefTypeName_ = tdt->getDecl()->getQualifiedNameAsString();
+ isTypedefType_ = true;
+ return true;
+ }
+
+ bool VisitBuiltinType(clang::BuiltinType *builtinType) {
+ builtinType_ = builtinType;
+ return true;
+ }
+
+ bool VisitComplexType(clang::ComplexType *) {
+ isComplexType_ = true;
+ return true;
+ }
+
+ bool VisitPointerType(clang::PointerType *) {
+ ++pointerCount_;
+ return true;
+ }
+
+ bool VisitArrayType(clang::ArrayType *) {
+ ++pointerCount_;
+ return true;
+ }
+
+ // passed qual type
+ const clang::QualType qualType_;
+
+ bool isTypedefType() const { return isTypedefType_; }
+ bool isComplexType() const { return isComplexType_; }
+ const std::string typedefTypeName() const & { return typedefTypeName_; }
+ const clang::BuiltinType *builtinType() const { return builtinType_; }
+ size_t pointerCount() const { return pointerCount_; }
+
+private:
+ bool isTypedefType_{false};
+ bool isComplexType_{false};
+ std::string typedefTypeName_;
+ size_t pointerCount_{0};
+
+ clang::BuiltinType *builtinType_{nullptr};
+};
+
+} // end of namespace: mpi
+#endif // end of include guard: TYPEVISITOR_HPP_KOZYUVZH
Index: tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/Utility.cpp
===================================================================
--- tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/Utility.cpp (revision 0)
+++ tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/Utility.cpp (working copy)
@@ -0,0 +1,113 @@
+//===-- Utility.cpp - utility functions -------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Utility functions for MPI-Checker.
+///
+//===----------------------------------------------------------------------===//
+
+#include "clang/Lex/Lexer.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "Utility.hpp"
+#include <sstream>
+#include <vector>
+
+namespace util {
+
+/// \brief Split string by delimiter helper function.
+static std::vector<std::string> &split(const std::string &s, char delim,
+ std::vector<std::string> &elems) {
+ std::stringstream ss(s);
+ std::string item;
+ while (std::getline(ss, item, delim)) {
+ elems.push_back(item);
+ }
+ return elems;
+}
+
+std::vector<std::string> split(const std::string &s, char delim) {
+ std::vector<std::string> elems;
+ split(s, delim, elems);
+ return elems;
+}
+
+clang::StringRef
+sourceRangeAsStringRef(const clang::SourceRange &sourceRange,
+ clang::ento::AnalysisManager &analysisManager) {
+ auto charSourceRange = clang::CharSourceRange::getTokenRange(sourceRange);
+ return clang::Lexer::getSourceText(charSourceRange,
+ analysisManager.getSourceManager(),
+ clang::LangOptions());
+}
+
+clang::SourceRange sourceRange(const clang::ento::MemRegion *memRegion) {
+ const clang::ento::VarRegion *varRegion =
+ clang::dyn_cast<clang::ento::VarRegion>(memRegion->getBaseRegion());
+
+ const clang::ento::FieldRegion *fieldRegion =
+ clang::dyn_cast<clang::ento::FieldRegion>(memRegion);
+
+ if (fieldRegion) {
+ return fieldRegion->getDecl()->getSourceRange();
+ } else if (varRegion) {
+ return varRegion->getDecl()->getSourceRange();
+ } else {
+ // non valid source range (can be checked by client)
+ return clang::SourceRange{};
+ }
+}
+
+std::string variableName(const clang::ento::MemRegion *memRegion) {
+ const clang::ento::VarRegion *varRegion =
+ clang::dyn_cast<clang::ento::VarRegion>(memRegion->getBaseRegion());
+
+ const clang::ento::FieldRegion *fieldRegion =
+ clang::dyn_cast<clang::ento::FieldRegion>(memRegion);
+
+ const clang::ento::ElementRegion *elementRegion =
+ memRegion->getAs<clang::ento::ElementRegion>();
+
+ std::string varName{""};
+
+ // members, fields
+ if (fieldRegion) {
+ varName = varRegion->getDecl()->getNameAsString() + "." +
+ fieldRegion->getDecl()->getNameAsString();
+ }
+ // variable
+ else if (varRegion) {
+ varName = varRegion->getDecl()->getNameAsString();
+ } else {
+ // get var-decl-name for symbolic region
+ }
+
+ if (elementRegion) {
+ llvm::APSInt indexInArray;
+ indexInArray = elementRegion->getIndex()
+ .getAs<clang::ento::nonloc::ConcreteInt>()
+ ->getValue();
+
+ llvm::SmallVector<char, 2> intValAsString;
+ indexInArray.toString(intValAsString);
+ std::string idx{intValAsString.begin(), intValAsString.end()};
+ return varName + "[" + idx + "]";
+ } else {
+ return varName;
+ }
+}
+
+const clang::IdentifierInfo *getIdentInfo(const clang::CallExpr *callExpr) {
+ if (callExpr->getDirectCallee()) {
+ return callExpr->getDirectCallee()->getIdentifier();
+ } else {
+ return nullptr;
+ }
+}
+
+} // end of namespace: util
Index: tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/Utility.hpp
===================================================================
--- tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/Utility.hpp (revision 0)
+++ tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/Utility.hpp (working copy)
@@ -0,0 +1,53 @@
+//===-- Utility.hpp - utility functions -------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Utility functions for MPI-Checker.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef UTILITY_HPP_SVQZWTL8
+#define UTILITY_HPP_SVQZWTL8
+
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+
+namespace util {
+
+/// \brief Returns part of the code specified by range unmodified as string ref.
+///
+/// \param source range
+/// \param analysis manager
+///
+/// \return code part as string ref
+clang::StringRef sourceRangeAsStringRef(const clang::SourceRange &,
+ clang::ento::AnalysisManager &);
+
+/// \brief Split string by delimiter.
+///
+/// \param string to split
+/// \param delimiter
+///
+/// \return string array split by delimiter
+std::vector<std::string> split(const std::string &, char);
+
+/// \brief Retrieve identifier info for a call expression.
+///
+/// Returns nullptr if there's no direct callee.
+///
+/// \param callExpr to retrieve ident info for
+///
+/// \return identifier info for passed call expression
+const clang::IdentifierInfo *getIdentInfo(const clang::CallExpr *);
+
+std::string variableName(const clang::ento::MemRegion *);
+clang::SourceRange sourceRange(const clang::ento::MemRegion *);
+
+} // end of namespace: util
+
+#endif // end of include guard: UTILITY_HPP_SVQZWTL8
Index: tools/clang/test/Analysis/MPIChecker.c
===================================================================
--- tools/clang/test/Analysis/MPIChecker.c (revision 0)
+++ tools/clang/test/Analysis/MPIChecker.c (working copy)
@@ -0,0 +1,462 @@
+// RUN: %clang_cc1 -I/usr/include/ -I/usr/local/include/ -analyze -analyzer-checker=mpi.MPI-Checker -verify %s
+
+#include <mpi.h>
+#include <complex.h>
+#include <stdint.h>
+
+/// Point-to-point calls of different test functions should not be matched
+/// -> set tags.
+
+void doubleWait() {
+ int rank = 0;
+ double buf = 0;
+ MPI_Comm_rank(MPI_COMM_WORLD, &rank);
+
+ if (rank > 0) {
+ MPI_Request req[2];
+
+ MPI_Isend(&buf, 1, MPI_DOUBLE, rank + 1, 1, MPI_COMM_WORLD, &req[0]);
+ MPI_Irecv(&buf, 1, MPI_DOUBLE, rank - 1, 1, MPI_COMM_WORLD, &req[1]);
+
+ MPI_Wait(&req[0], MPI_STATUS_IGNORE);
+ MPI_Waitall(2, req, MPI_STATUS_IGNORE); // expected-warning{{Request 'req[0]' is already waited upon by 'MPI_Wait' in line 21.}}
+ }
+}
+
+void doubleWait2() {
+ int rank = 0;
+ double buf = 0;
+ MPI_Comm_rank(MPI_COMM_WORLD, &rank);
+ if (rank != 0) {
+ MPI_Request sendReq1, recvReq1;
+
+ MPI_Isend(&buf, 1, MPI_DOUBLE, rank + 1, 2, MPI_COMM_WORLD, &sendReq1);
+ MPI_Irecv(&buf, 1, MPI_DOUBLE, rank - 1, 2, MPI_COMM_WORLD, &recvReq1);
+ MPI_Wait(&sendReq1, MPI_STATUS_IGNORE);
+ MPI_Wait(&recvReq1, MPI_STATUS_IGNORE);
+ MPI_Wait(&recvReq1, MPI_STATUS_IGNORE); // expected-warning{{Request 'recvReq1' is already waited upon by 'MPI_Wait' in line 36.}}
+ }
+}
+
+void doubleWait3() {
+ typedef struct { MPI_Request req; } ReqStruct;
+
+ ReqStruct rs;
+ int rank = 0;
+ double buf = 0;
+ MPI_Comm_rank(MPI_COMM_WORLD, &rank);
+
+ MPI_Ireduce(MPI_IN_PLACE, &buf, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD,
+ &rs.req);
+ MPI_Wait(&rs.req, MPI_STATUS_IGNORE);
+ MPI_Wait(&rs.req, MPI_STATUS_IGNORE); // expected-warning{{Request 'rs.req' is already waited upon by 'MPI_Wait' in line 51.}}
+}
+
+void missingWait() {
+ int rank = 0;
+ double buf = 0;
+ MPI_Comm_rank(MPI_COMM_WORLD, &rank);
+ if (rank == 0) {
+ } else {
+ MPI_Request sendReq1, recvReq1;
+
+ MPI_Isend(&buf, 1, MPI_DOUBLE, rank + 1, 3, MPI_COMM_WORLD, &sendReq1);
+ MPI_Irecv(&buf, 1, MPI_DOUBLE, rank - 1, 3, MPI_COMM_WORLD, &recvReq1);
+ MPI_Wait(&recvReq1, MPI_STATUS_IGNORE);
+ }
+} // expected-warning{{'MPI_Isend' in line 63, using request 'sendReq1', has no matching wait in the scope of this function.}}
+void matchedWait1() {
+ int rank = 0;
+ double buf = 0;
+ MPI_Comm_rank(MPI_COMM_WORLD, &rank);
+ if (rank >= 0) {
+ MPI_Request sendReq1, recvReq1;
+ MPI_Isend(&buf, 1, MPI_DOUBLE, rank + 1, 21, MPI_COMM_WORLD, &sendReq1);
+ MPI_Irecv(&buf, 1, MPI_DOUBLE, rank - 1, 21, MPI_COMM_WORLD, &recvReq1);
+
+ MPI_Wait(&sendReq1, MPI_STATUS_IGNORE);
+ MPI_Wait(&recvReq1, MPI_STATUS_IGNORE);
+ }
+} // no error
+
+void matchedWait2() {
+ int rank = 0;
+ double buf = 0;
+ MPI_Comm_rank(MPI_COMM_WORLD, &rank);
+ if (rank >= 0) {
+ MPI_Request sendReq1, recvReq1;
+ MPI_Isend(&buf, 1, MPI_DOUBLE, rank + 1, 22, MPI_COMM_WORLD, &sendReq1);
+ MPI_Irecv(&buf, 1, MPI_DOUBLE, rank - 1, 22, MPI_COMM_WORLD, &recvReq1);
+ MPI_Wait(&sendReq1, MPI_STATUS_IGNORE);
+ MPI_Wait(&recvReq1, MPI_STATUS_IGNORE);
+ }
+} // no error
+
+void matchedWait3() {
+ int rank = 0;
+ double buf = 0;
+ MPI_Comm_rank(MPI_COMM_WORLD, &rank);
+ if (rank >= 0) {
+ MPI_Request sendReq1, recvReq1;
+ MPI_Isend(&buf, 1, MPI_DOUBLE, rank + 1, 23, MPI_COMM_WORLD, &sendReq1);
+ MPI_Irecv(&buf, 1, MPI_DOUBLE, rank - 1, 23, MPI_COMM_WORLD, &recvReq1);
+
+ if (rank > 1000) {
+ MPI_Wait(&sendReq1, MPI_STATUS_IGNORE);
+ MPI_Wait(&recvReq1, MPI_STATUS_IGNORE);
+ } else {
+ MPI_Wait(&sendReq1, MPI_STATUS_IGNORE);
+ MPI_Wait(&recvReq1, MPI_STATUS_IGNORE);
+ }
+ }
+} // no error
+
+void doubleNonblocking() {
+ int rank = 0;
+ double buf = 0;
+ MPI_Comm_rank(MPI_COMM_WORLD, &rank);
+ if (rank == 1) {
+ } else {
+ MPI_Request sendReq1;
+
+ MPI_Isend(&buf, 1, MPI_DOUBLE, rank + 1, 4, MPI_COMM_WORLD, &sendReq1);
+ MPI_Irecv(&buf, 1, MPI_DOUBLE, rank - 1, 4, MPI_COMM_WORLD, &sendReq1); // expected-warning{{Request 'sendReq1' is already in use by nonblocking call 'MPI_Isend' in line 122.}}
+ MPI_Wait(&sendReq1, MPI_STATUS_IGNORE);
+ }
+}
+
+void doubleNonblocking2() {
+ int rank = 0;
+ double buf = 0;
+ MPI_Comm_rank(MPI_COMM_WORLD, &rank);
+
+ MPI_Request req;
+ MPI_Ireduce(MPI_IN_PLACE, &buf, 1, MPI_DOUBLE, MPI_SUM, 5, MPI_COMM_WORLD, &req);
+ MPI_Ireduce(MPI_IN_PLACE, &buf, 1, MPI_DOUBLE, MPI_SUM, 5, MPI_COMM_WORLD, &req); // expected-warning{{Request 'req' is already in use by nonblocking call 'MPI_Ireduce' in line 134.}}
+ MPI_Wait(&req, MPI_STATUS_IGNORE);
+}
+
+void doubleNonblocking3() {
+ typedef struct { MPI_Request req; } ReqStruct;
+
+ ReqStruct rs;
+ int rank = 0;
+ double buf = 0;
+ MPI_Comm_rank(MPI_COMM_WORLD, &rank);
+
+ MPI_Ireduce(MPI_IN_PLACE, &buf, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD, &rs.req);
+ MPI_Ireduce(MPI_IN_PLACE, &buf, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD, &rs.req); // expected-warning{{Request 'rs.req' is already in use by nonblocking call 'MPI_Ireduce' in line 147.}}
+ MPI_Wait(&rs.req, MPI_STATUS_IGNORE);
+}
+
+void missingNonBlocking() {
+ int rank = 0;
+ MPI_Comm_rank(MPI_COMM_WORLD, &rank);
+ if (rank == 1) {
+ MPI_Request sendReq1;
+ MPI_Wait(&sendReq1, MPI_STATUS_IGNORE); // expected-warning{{Request 'sendReq1' has no matching nonblocking call.}}
+ }
+}
+
+void noDoubleRequestUsage() {
+ typedef struct {
+ MPI_Request req;
+ MPI_Request req2;
+ } ReqStruct;
+
+ ReqStruct rs;
+ int rank = 0;
+ double buf = 0;
+ MPI_Comm_rank(MPI_COMM_WORLD, &rank);
+
+ MPI_Ireduce(MPI_IN_PLACE, &buf, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD,
+ &rs.req);
+ MPI_Ireduce(MPI_IN_PLACE, &buf, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD,
+ &rs.req2);
+ MPI_Wait(&rs.req, MPI_STATUS_IGNORE);
+ MPI_Wait(&rs.req2, MPI_STATUS_IGNORE);
+}
+
+void noDoubleRequestUsage2() {
+ typedef struct {
+ MPI_Request req[2];
+ MPI_Request req2;
+ } ReqStruct;
+
+ ReqStruct rs;
+ int rank = 0;
+ double buf = 0;
+ MPI_Comm_rank(MPI_COMM_WORLD, &rank);
+
+ MPI_Ireduce(MPI_IN_PLACE, &buf, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD,
+ &rs.req[0]);
+ MPI_Ireduce(MPI_IN_PLACE, &buf, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD,
+ &rs.req[1]);
+ MPI_Ireduce(MPI_IN_PLACE, &buf, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD,
+ &rs.req2);
+ MPI_Wait(&rs.req[0], MPI_STATUS_IGNORE);
+ MPI_Wait(&rs.req[1], MPI_STATUS_IGNORE);
+ MPI_Wait(&rs.req2, MPI_STATUS_IGNORE);
+}
+
+void nestedRequest() {
+ typedef struct {
+ MPI_Request req[2];
+ MPI_Request req2;
+ } ReqStruct;
+
+ ReqStruct rs;
+ int rank = 0;
+ double buf = 0;
+ MPI_Comm_rank(MPI_COMM_WORLD, &rank);
+
+ MPI_Ireduce(MPI_IN_PLACE, &buf, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD,
+ &rs.req[0]);
+ MPI_Ireduce(MPI_IN_PLACE, &buf, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD,
+ &rs.req[1]);
+ MPI_Ireduce(MPI_IN_PLACE, &buf, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD,
+ &rs.req2);
+ MPI_Waitall(2, rs.req, MPI_STATUSES_IGNORE);
+ MPI_Wait(&rs.req2, MPI_STATUS_IGNORE);
+}
+
+void singleRequestInWaitall() {
+ MPI_Request r;
+ int rank = 0;
+ double buf = 0;
+ MPI_Comm_rank(MPI_COMM_WORLD, &rank);
+
+ MPI_Ireduce(MPI_IN_PLACE, &buf, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD,
+ &r);
+ MPI_Waitall(1, &r, MPI_STATUSES_IGNORE);
+}
+
+// same tag is used for all type matching functions
+void typeMatching1() {
+ double buf = 0;
+ double *bufP = &buf;
+ int rank = 0;
+ MPI_Comm_rank(MPI_COMM_WORLD, &rank);
+
+ if (rank == 0) {
+ MPI_Send(&buf, 1, MPI_FLOAT, rank + 1, 0, MPI_COMM_WORLD); // expected-warning{{Buffer type 'double' and specified MPI type 'MPI_FLOAT' do not match.}}
+ } else {
+ MPI_Recv(bufP, 1, MPI_FLOAT, rank - 1, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE); // expected-warning{{Buffer type 'double' and specified MPI type 'MPI_FLOAT' do not match.}}
+ }
+
+ if (rank == 0) {
+ MPI_Send(&buf, 1, MPI_DOUBLE, rank + 1, 0, MPI_COMM_WORLD);
+ } else {
+ MPI_Recv(bufP, 1, MPI_DOUBLE, rank - 1, 0, MPI_COMM_WORLD,
+ MPI_STATUS_IGNORE);
+ }
+}
+
+void typeMatching2() {
+ int buf = 0;
+ int *bufP = &buf;
+ MPI_Reduce(MPI_IN_PLACE, &buf, 1, MPI_CHAR, MPI_SUM, 0, MPI_COMM_WORLD); // expected-warning{{Buffer type 'int' and specified MPI type 'MPI_CHAR' do not match.}}
+ MPI_Reduce(MPI_IN_PLACE, bufP, 1, MPI_CHAR, MPI_SUM, 0, MPI_COMM_WORLD); // expected-warning{{Buffer type 'int' and specified MPI type 'MPI_CHAR' do not match.}}
+
+ MPI_Reduce(MPI_IN_PLACE, &buf, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD);
+ MPI_Reduce(MPI_IN_PLACE, bufP, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD);
+}
+
+void typeMatching3() {
+ long double buf = 11;
+ const long double *const bufP = &buf;
+ int rank = 0;
+ MPI_Comm_rank(MPI_COMM_WORLD, &rank);
+ if (rank == 0) {
+ MPI_Send(bufP, 1, MPI_DOUBLE, rank + 1, 0, MPI_COMM_WORLD); // expected-warning{{Buffer type 'long double' and specified MPI type 'MPI_DOUBLE' do not match.}}
+ } else {
+ MPI_Recv(&buf, 1, MPI_DOUBLE, rank - 1, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE); // expected-warning{{Buffer type 'long double' and specified MPI type 'MPI_DOUBLE' do not match.}}
+ }
+
+ if (rank == 0) {
+ MPI_Send(bufP, 1, MPI_LONG_DOUBLE, rank + 1, 0, MPI_COMM_WORLD);
+ } else {
+ MPI_Recv(&buf, 1, MPI_LONG_DOUBLE, rank - 1, 0, MPI_COMM_WORLD,
+ MPI_STATUS_IGNORE);
+ }
+}
+
+void typeMatching4() {
+ long double _Complex buf = 11;
+ long double _Complex *bufP = &buf;
+ MPI_Reduce(MPI_IN_PLACE, &buf, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); // expected-warning{{Buffer type '_Complex long double' and specified MPI type 'MPI_DOUBLE' do not match.}}
+ MPI_Reduce(MPI_IN_PLACE, bufP, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); // expected-warning{{Buffer type '_Complex long double' and specified MPI type 'MPI_DOUBLE' do not match.}}
+
+ MPI_Reduce(MPI_IN_PLACE, &buf, 1, MPI_C_LONG_DOUBLE_COMPLEX, MPI_SUM, 0,
+ MPI_COMM_WORLD);
+ MPI_Reduce(MPI_IN_PLACE, bufP, 1, MPI_C_LONG_DOUBLE_COMPLEX, MPI_SUM, 0,
+ MPI_COMM_WORLD);
+}
+
+void typeMatching5() {
+ int64_t buf = 11;
+ const int64_t *const bufP = &buf;
+ int rank = 0;
+ MPI_Comm_rank(MPI_COMM_WORLD, &rank);
+ if (rank == 0) {
+ MPI_Send(bufP, 1, MPI_INT, rank + 1, 0, MPI_COMM_WORLD); // expected-warning{{Buffer type 'int64_t' and specified MPI type 'MPI_INT' do not match.}}
+ } else {
+ MPI_Recv(&buf, 1, MPI_INT, rank - 1, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE); // expected-warning{{Buffer type 'int64_t' and specified MPI type 'MPI_INT' do not match.}}
+ }
+}
+
+void typeMatching6() {
+ uint8_t buf = 11;
+ uint8_t *bufP = &buf;
+ MPI_Reduce(MPI_IN_PLACE, &buf, 1, MPI_UNSIGNED, MPI_SUM, 0, MPI_COMM_WORLD); // expected-warning{{Buffer type 'uint8_t' and specified MPI type 'MPI_UNSIGNED' do not match.}}
+ MPI_Reduce(MPI_IN_PLACE, bufP, 1, MPI_UNSIGNED, MPI_SUM, 0, MPI_COMM_WORLD); // expected-warning{{Buffer type 'uint8_t' and specified MPI type 'MPI_UNSIGNED' do not match.}}
+
+ MPI_Reduce(MPI_IN_PLACE, &buf, 1, MPI_UINT8_T, MPI_SUM, 0, MPI_COMM_WORLD);
+ MPI_Reduce(MPI_IN_PLACE, bufP, 1, MPI_UINT8_T, MPI_SUM, 0, MPI_COMM_WORLD);
+}
+
+void typeMatching7() {
+ uint8_t buf = 11;
+ const uint8_t *const bufP = &buf;
+
+ int rank = 0;
+ MPI_Comm_rank(MPI_COMM_WORLD, &rank);
+
+ if (rank == 0) {
+ MPI_Send(bufP, 1, MPI_UINT16_T, rank + 1, 0, MPI_COMM_WORLD); // expected-warning{{Buffer type 'uint8_t' and specified MPI type 'MPI_UINT16_T' do not match.}}
+ } else {
+ MPI_Recv(&buf, 1, MPI_UINT16_T, rank - 1, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE); // expected-warning{{Buffer type 'uint8_t' and specified MPI type 'MPI_UINT16_T' do not match.}}
+ }
+
+ if (rank == 0) {
+ MPI_Send(bufP, 1, MPI_UINT8_T, rank + 1, 0, MPI_COMM_WORLD);
+ } else {
+ MPI_Recv(&buf, 1, MPI_UINT8_T, rank - 1, 0, MPI_COMM_WORLD,
+ MPI_STATUS_IGNORE);
+ }
+}
+
+void typeMatching8() {
+ uint8_t buf = 11;
+ uint8_t *bufP = &buf;
+ MPI_Reduce(MPI_IN_PLACE, &buf, 1, MPI_INT8_T, MPI_SUM, 0, MPI_COMM_WORLD); // expected-warning{{Buffer type 'uint8_t' and specified MPI type 'MPI_INT8_T' do not match.}}
+ MPI_Reduce(MPI_IN_PLACE, bufP, 1, MPI_INT8_T, MPI_SUM, 0, MPI_COMM_WORLD); // expected-warning{{Buffer type 'uint8_t' and specified MPI type 'MPI_INT8_T' do not match.}}
+
+ MPI_Reduce(MPI_IN_PLACE, &buf, 1, MPI_UINT8_T, MPI_SUM, 0, MPI_COMM_WORLD);
+ MPI_Reduce(MPI_IN_PLACE, bufP, 1, MPI_UINT8_T, MPI_SUM, 0, MPI_COMM_WORLD);
+}
+
+void typeMatching9() {
+ char buf = 'a';
+ char *bufP = &buf;
+ MPI_Reduce(MPI_IN_PLACE, &buf, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD); // expected-warning{{Buffer type 'char' and specified MPI type 'MPI_INT' do not match.}}
+ MPI_Reduce(MPI_IN_PLACE, bufP, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD); // expected-warning{{Buffer type 'char' and specified MPI type 'MPI_INT' do not match.}}
+
+ MPI_Reduce(MPI_IN_PLACE, &buf, 1, MPI_CHAR, MPI_SUM, 0, MPI_COMM_WORLD);
+ MPI_Reduce(MPI_IN_PLACE, bufP, 1, MPI_CHAR, MPI_SUM, 0, MPI_COMM_WORLD);
+}
+
+void typeMatching10() {
+ struct a {
+ int x;
+ } buf;
+ MPI_Reduce(MPI_IN_PLACE, &buf, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD);
+} // no error, checker does not verify structs
+
+void typeMatching11() {
+ float ***buf = NULL;
+ MPI_Reduce(MPI_IN_PLACE, **buf, 1, MPI_FLOAT, MPI_SUM, 0, MPI_COMM_WORLD);
+} // no error
+
+void typeMatching12() {
+ typedef int Int;
+ Int buf = 1;
+ MPI_Reduce(MPI_IN_PLACE, &buf, 1, MPI_CHAR, MPI_SUM, 0, MPI_COMM_WORLD);
+} // no error, checker makes no assumptions about typedefs
+
+void typeMatching13() {
+ long buf = 0;
+ MPI_Reduce(MPI_IN_PLACE, &buf, 8, MPI_BYTE, MPI_SUM, 0, MPI_COMM_WORLD);
+} // no error, checker does not verify MPI_BYTE
+
+void typeMatching14() {
+ float ***buf = NULL;
+ MPI_Reduce(MPI_IN_PLACE, buf, 1, MPI_FLOAT, MPI_SUM, 0, MPI_COMM_WORLD); // expected-warning{{Buffer is not correctly dereferenced. It is passed as a *** pointer.}}
+} // buffer type not correctly dereferenced
+
+void typeMatching15() {
+ float *buf = NULL;
+ MPI_Reduce(MPI_IN_PLACE, &buf, 1, MPI_FLOAT, MPI_SUM, 0, MPI_COMM_WORLD); // expected-warning{{Buffer is not correctly dereferenced. It is passed as a ** pointer.}}
+} // buffer type is float **
+
+void typeMatching16() {
+ float ***buf = NULL;
+ MPI_Reduce(MPI_IN_PLACE, *buf, 1, MPI_FLOAT, MPI_SUM, 0, MPI_COMM_WORLD); // expected-warning{{Buffer is not correctly dereferenced. It is passed as a ** pointer.}}
+} // buffer type not correctly dereferenced
+
+void typeMatching17() {
+ float buf[2];
+ MPI_Reduce(MPI_IN_PLACE, buf, 2, MPI_FLOAT, MPI_SUM, 0, MPI_COMM_WORLD);
+}
+
+void typeMatching18() {
+ float *buf[2];
+ MPI_Reduce(MPI_IN_PLACE, buf, 2, MPI_FLOAT, MPI_SUM, 0, MPI_COMM_WORLD); // expected-warning{{Buffer is not correctly dereferenced. It is passed as a ** pointer.}}
+}
+
+void typeMatching19() {
+ float *buf[2];
+ MPI_Reduce(MPI_IN_PLACE, buf, 2, MPI_FLOAT, MPI_SUM, 0, MPI_COMM_WORLD); // expected-warning{{Buffer is not correctly dereferenced. It is passed as a ** pointer.}}
+}
+
+void typeMatching20() {
+ float *buf = NULL;
+ MPI_Reduce(MPI_IN_PLACE, &buf[0], 1, MPI_FLOAT, MPI_SUM, 0, MPI_COMM_WORLD);
+ MPI_Bcast(&buf[0], 21, MPI_FLOAT, 0, MPI_COMM_WORLD);
+}
+
+void invalidArgType() {
+ int rank = 0;
+ int buf = 0;
+ MPI_Comm_rank(MPI_COMM_WORLD, &rank);
+ if (rank == 0) {
+ MPI_Send(&buf, 1, MPI_INT, rank + 1.1, 17, MPI_COMM_WORLD); // expected-warning{{The type, argument at index 3 evaluates to, is not an integer type.}}
+ } else if (rank == 1) {
+ MPI_Recv(&buf, 1, MPI_INT, rank - 1.1, 17, MPI_COMM_WORLD, MPI_STATUS_IGNORE); // expected-warning{{The type, argument at index 3 evaluates to, is not an integer type.}}
+ }
+}
+
+void invalidArgType2() {
+ int rank = 0;
+ int buf = 0;
+ MPI_Comm_rank(MPI_COMM_WORLD, &rank);
+ if (rank == 0) {
+ MPI_Send(&buf, 1 + 1.1, MPI_INT, rank + 1, 18, MPI_COMM_WORLD); // expected-warning{{The type, argument at index 1 evaluates to, is not an integer type.}}
+ } else if (rank == 1) {
+ MPI_Recv(&buf, 1 + 1.1, MPI_INT, rank - 1, 18, MPI_COMM_WORLD, MPI_STATUS_IGNORE); // expected-warning{{The type, argument at index 1 evaluates to, is not an integer type.}}
+ }
+}
+
+void invalidArgType3() {
+ int rank = 0;
+ int buf = 0;
+ double x = 1.1;
+ MPI_Comm_rank(MPI_COMM_WORLD, &rank);
+ if (rank == 0) {
+ MPI_Send(&buf, 1 + x, MPI_INT, rank + 1, 18, MPI_COMM_WORLD); // expected-warning{{The type, argument at index 1 evaluates to, is not an integer type.}}
+ } else if (rank == 1) {
+ MPI_Recv(&buf, 1 + x, MPI_INT, rank - 1, 18, MPI_COMM_WORLD, MPI_STATUS_IGNORE); // expected-warning{{The type, argument at index 1 evaluates to, is not an integer type.}}
+ }
+}
+
+double d() { return 1.1; }
+void invalidArgType4() {
+ int rank = 0;
+ int buf = 0;
+ MPI_Comm_rank(MPI_COMM_WORLD, &rank);
+ if (rank == 0) {
+ MPI_Send(&buf, 1, MPI_INT, rank + d(), 18, MPI_COMM_WORLD); // expected-warning{{The type, argument at index 3 evaluates to, is not an integer type.}}
+ } else if (rank == 1) {
+ MPI_Recv(&buf, 1, MPI_INT, rank - d(), 18, MPI_COMM_WORLD, MPI_STATUS_IGNORE); // expected-warning{{The type, argument at index 3 evaluates to, is not an integer type.}}
+ }
+}
Index: tools/clang/unittests/StaticAnalyzer/CMakeLists.txt
===================================================================
--- tools/clang/unittests/StaticAnalyzer/CMakeLists.txt (revision 247122)
+++ tools/clang/unittests/StaticAnalyzer/CMakeLists.txt (working copy)
@@ -6,8 +6,10 @@
AnalyzerOptionsTest.cpp
)
+add_subdirectory(MPI-Checker)
+
target_link_libraries(StaticAnalysisTests
clangBasic
clangAnalysis
- clangStaticAnalyzerCore
+ clangStaticAnalyzerCore
)
Index: tools/clang/unittests/StaticAnalyzer/MPI-Checker/CMakeLists.txt
===================================================================
--- tools/clang/unittests/StaticAnalyzer/MPI-Checker/CMakeLists.txt (revision 0)
+++ tools/clang/unittests/StaticAnalyzer/MPI-Checker/CMakeLists.txt (working copy)
@@ -0,0 +1,18 @@
+set(LLVM_LINK_COMPONENTS Support)
+
+include_directories(
+ ../../../lib/StaticAnalyzer/Checkers/MPI-Checker
+ )
+
+add_clang_unittest(MPI-Checker
+ UtilityTest.cpp
+ ContainerTest.cpp
+ )
+
+target_link_libraries(MPI-Checker
+ clangAST
+ clangBasic
+ clangLex
+ clangParse
+ clangSema
+ clangStaticAnalyzerCheckers)
Index: tools/clang/unittests/StaticAnalyzer/MPI-Checker/ContainerTest.cpp
===================================================================
--- tools/clang/unittests/StaticAnalyzer/MPI-Checker/ContainerTest.cpp (revision 0)
+++ tools/clang/unittests/StaticAnalyzer/MPI-Checker/ContainerTest.cpp (working copy)
@@ -0,0 +1,105 @@
+#include "gtest/gtest.h"
+#include <vector>
+#include "Container.hpp"
+
+TEST(Container, isContained) {
+ std::vector<int> v{0, 1, 2, 3, 4, 5, 6, 8, 9};
+ EXPECT_TRUE(cont::isContained(v, 0));
+ EXPECT_TRUE(cont::isContained(v, 3));
+ EXPECT_TRUE(cont::isContained(v, 9));
+}
+
+TEST(Container, isNotContained) {
+ std::vector<int> v{1, 2, 4, 5, 6, 8};
+ EXPECT_FALSE(cont::isContained(v, 0));
+ EXPECT_FALSE(cont::isContained(v, 3));
+ EXPECT_FALSE(cont::isContained(v, 9));
+}
+
+TEST(Container, isContainedPred) {
+ std::vector<int> v{0, 1, 2, 3, 4, 5, 6, 8, 9};
+ EXPECT_TRUE(cont::isContainedPred(v, [](int x) { return x == 0; }));
+ EXPECT_TRUE(cont::isContainedPred(v, [](int x) { return x == 3; }));
+ EXPECT_TRUE(cont::isContainedPred(v, [](int x) { return x == 9; }));
+}
+
+TEST(Container, isNotContainedPred) {
+ std::vector<int> v{1, 2, 4, 5, 6, 8};
+ EXPECT_FALSE(cont::isContainedPred(v, [](int x) { return x == 0; }));
+ EXPECT_FALSE(cont::isContainedPred(v, [](int x) { return x == 3; }));
+ EXPECT_FALSE(cont::isContainedPred(v, [](int x) { return x == 9; }));
+}
+
+TEST(Container, erase) {
+ std::vector<int> v{0, 1, 2, 3, 4, 5, 6, 8, 9};
+ int x = 0, y = 3, z = 9;
+
+ cont::erase(v, x);
+ std::vector<int> comp{1, 2, 3, 4, 5, 6, 8, 9};
+ EXPECT_EQ(v, comp);
+
+ cont::erase(v, y);
+ comp = {1, 2, 4, 5, 6, 8, 9};
+ EXPECT_EQ(v, comp);
+
+ cont::erase(v, z);
+ comp = {1, 2, 4, 5, 6, 8};
+ EXPECT_EQ(v, comp);
+
+ std::vector<int> v2{0, 1, 1, 3, 4, 5, 6, 8, 9};
+ std::vector<int> comp2{0, 1, 3, 4, 5, 6, 8, 9};
+ int x2 = 1;
+ cont::erase(v2, x2);
+ EXPECT_EQ(v2, comp2);
+}
+
+TEST(Container, eraseAll) {
+ std::vector<int> v{1, 1, 2, 3, 1, 5, 6, 8, 9};
+ int x = 1;
+ cont::eraseAll(v, x);
+
+ EXPECT_FALSE(cont::isContained(v, x));
+
+ std::vector<int> comp{2, 3, 5, 6, 8, 9};
+ EXPECT_EQ(v, comp);
+}
+
+TEST(Container, erasePred) {
+ std::vector<int> v{1, 1, 2, 3, 1, 5, 6, 8, 9};
+ cont::erasePred(v, [](int x) { return x == 1; });
+ std::vector<int> comp{1, 2, 3, 1, 5, 6, 8, 9};
+ EXPECT_EQ(v, comp);
+
+ cont::erasePred(v, [](int x) { return x == 1; });
+ comp = {2, 3, 1, 5, 6, 8, 9};
+ EXPECT_EQ(v, comp);
+
+ cont::erasePred(v, [](int x) { return x == 9; });
+ comp = {2, 3, 1, 5, 6, 8};
+ EXPECT_EQ(v, comp);
+}
+
+TEST(Container, eraseIndex) {
+ std::vector<int> v{0, 1, 2, 3};
+ cont::eraseIndex(v, 1);
+ std::vector<int> comp{0, 2, 3};
+ EXPECT_EQ(v, comp);
+}
+
+TEST(Container, copy) {
+ std::vector<int> v{0, 1};
+ std::vector<int> v2{2, 3};
+ std::vector<int> comp{0, 1, 2, 3};
+ cont::copy(v2, v);
+ EXPECT_EQ(v, comp);
+}
+
+TEST(Container, isPermutation) {
+ std::vector<int> v1{0, 1, 2, 3};
+ std::vector<int> v2{1, 0, 3, 2};
+
+ EXPECT_TRUE(cont::isPermutation(v1, v2));
+
+ std::vector<int> v3{1, 0, 4, 2};
+ EXPECT_FALSE(cont::isPermutation(v1, v3));
+}
Index: tools/clang/unittests/StaticAnalyzer/MPI-Checker/UtilityTest.cpp
===================================================================
--- tools/clang/unittests/StaticAnalyzer/MPI-Checker/UtilityTest.cpp (revision 0)
+++ tools/clang/unittests/StaticAnalyzer/MPI-Checker/UtilityTest.cpp (working copy)
@@ -0,0 +1,12 @@
+#include "gtest/gtest.h"
+#include "Utility.hpp"
+
+TEST(Utility, split) {
+ auto s = util::split("aaa:bbb", ':');
+ EXPECT_EQ(s[0], "aaa");
+ EXPECT_EQ(s[1], "bbb");
+
+ auto s2 = util::split("aaa,bbb", ',');
+ EXPECT_EQ(s2[0], "aaa");
+ EXPECT_EQ(s2[1], "bbb");
+}
More information about the cfe-commits
mailing list