Index: tools/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt =================================================================== --- tools/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt +++ tools/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt @@ -46,6 +46,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 +++ tools/clang/lib/StaticAnalyzer/Checkers/Checkers.td @@ -27,6 +27,8 @@ def DeadCode : Package<"deadcode">; def DeadCodeAlpha : Package<"deadcode">, InPackage, Hidden; +def MPI : Package<"mpi">; + def Security : Package <"security">; def InsecureAPI : Package<"insecureAPI">, InPackage; def SecurityAlpha : Package<"security">, InPackage, Hidden; @@ -520,6 +522,13 @@ DescFile<"ObjCContainersChecker.cpp">; } + +let ParentPackage = MPI in { +def MPIChecker : Checker<"MPI-Checker">, + HelpText<"Checks MPI code">, + DescFile<"MPIChecker.cpp">; +} // end "MPI" + //===----------------------------------------------------------------------===// // Checkers for LLVM development. //===----------------------------------------------------------------------===// Index: tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/Container.h =================================================================== --- tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/Container.h +++ tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/Container.h @@ -0,0 +1,31 @@ +//===-- Container.h - 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 +/// This file defines convenience templates for C++ container class usage. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MPICHECKER_CONTAINER_H +#define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MPICHECKER_CONTAINER_H + +#include + +namespace cont { + +/// Returns true if \p Container contains \p Element. +template +bool contains(const T &Container, const E &Element) { + return std::find(Container.begin(), Container.end(), Element) != + Container.end(); +} + +} // end of namespace: cont + +#endif Index: tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.h =================================================================== --- tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.h +++ tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.h @@ -0,0 +1,171 @@ +//===-- MPIBugReporter.h - 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 +/// This file defines prefabricated reports which are emitted in +/// case of MPI related bugs, detected by AST-based and path-sensitive analysis. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MPICHECKER_MPIBUGREPORTER_H +#define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MPICHECKER_MPIBUGREPORTER_H + +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "MPITypes.h" +#include "MPIFunctionClassifier.h" + +namespace clang { +namespace mpi { + +class MPIBugReporter { +public: + MPIBugReporter(clang::ento::BugReporter &BR, + const clang::ento::CheckerBase &CB, + clang::ento::AnalysisManager &AM) + : BugReporter{BR}, CkrBase{CB}, AnalysisManager{AM}, FuncClassifier{AM} { + DoubleWaitBugType.reset( + new clang::ento::BugType(&CB, "Double wait", MPIError)); + UnmatchedWaitBugType.reset( + new clang::ento::BugType(&CB, "Unmatched wait", MPIError)); + DoubleNonblockingBugType.reset( + new clang::ento::BugType(&CB, "Double nonblocking", MPIError)); + MissingWaitBugType.reset( + new clang::ento::BugType(&CB, "Missing wait", MPIError)); + } + + // ast reports –––––––––––––––––––––––––––––––––––––––––––––––––––––––––– + + /// Report mismatch between buffer type and MPI datatype. + /// + /// \param MPICallExpr MPI call to report the mismatch for + /// \param IdxPair buffer type, MPI type index pair of the mismatch + /// \param BufferType buffer type + /// \param MPIType MPI datatype as string + void reportTypeMismatch(const clang::CallExpr *MPICallExpr, + const std::pair &IdxPair, + const std::string &BufferTypeName, + std::string MPIType) const; + + /// Report if a buffer is not passed as a single pointer. + /// + /// \param MPICallExpr MPI call to report incorrect buffer referencing for + /// \param Idx index of incorrectly referenced buffer + /// \param PointerCount pointer count + void reportIncorrectBufferReferencing(const clang::CallExpr *MPICallExpr, + size_t Idx, size_t PointerCount) const; + + /// Report non-integer type usage at indices where not allowed. (e.g. rank) + /// + /// \param MPICallExpr MPI call to report the invalid argument type for + /// \param Idx argument index of invalid argument type + void reportInvalidArgumentType(const clang::CallExpr *const MPICallExpr, + const size_t Idx) const; + + // path-sensitive reports ––––––––––––––––––––––––––––––––––––––––––––––– + + /// Report duplicate request use by waits in sequence without intermediate + /// nonblocking call. + /// + /// \param MPICallExpr MPI call that caused the double wait + /// \param Req request that was used by two waits in sequence + /// \param ExplNode node in the graph the bug appeared at + void reportDoubleWait(const clang::ento::CallEvent &MPICallExpr, + const Request &Req, + const clang::ento::ExplodedNode *const ExplNode) const; + + /// Report duplicate request use by nonblocking calls without intermediate + /// wait. + /// + /// \param MPICallExpr MPI call that caused the double nonblocking + /// \param Req request that was used by two nonblocking calls in sequence + /// \param ExplNode node in the graph the bug appeared at + void reportDoubleNonblocking( + const clang::ento::CallEvent &MPICallExpr, const Request &Req, + const clang::ento::ExplodedNode *const ExplNode) const; + + /// Report a missing wait for a nonblocking call. A missing wait report + /// is emitted if a nonblocking call is not matched in the scope of a + /// function. + /// + /// \param Req request that is not matched by a wait + /// \param ExplNode node in the graph the bug appeared at + void reportMissingWait(const Request &Req, + const clang::ento::ExplodedNode *const ExplNode) const; + + /// Report a wait on a request that has not been used at all before. + /// + /// \param CE wait call that uses the request + /// \param ReqRegion memory region of the request + /// \param ExplNode node in the graph the bug appeared at + void + reportUnmatchedWait(const clang::ento::CallEvent &CE, + const clang::ento::MemRegion *const ReqRegion, + const clang::ento::ExplodedNode *const ExplNode) const; + + const clang::Decl *CurrentFunctionDecl = nullptr; + +private: + /// Get line number for call event reference. + /// + /// \param CEREf call event reference + /// \returns line number as string + std::string lineNumber(const clang::ento::CallEventRef<> CERef) const; + + const std::string MPIError{"MPI Error"}; + + // path-sensitive bug types + std::unique_ptr UnmatchedWaitBugType; + std::unique_ptr MissingWaitBugType; + std::unique_ptr DoubleWaitBugType; + std::unique_ptr DoubleNonblockingBugType; + + clang::ento::BugReporter &BugReporter; + const clang::ento::CheckerBase &CkrBase; + clang::ento::AnalysisManager &AnalysisManager; + const clang::mpi::MPIFunctionClassifier FuncClassifier; + + ///--------------------------------------------------------------------- + /// A bug visitor is used to print extra diagnostics along the BugReport + /// path. In this case it is utilized, to include the second last wait on an + /// MPI request into the diagnostic. This class is an inner class of + /// MPIBugReporter. + class DoubleWaitVisitor + : public clang::ento::BugReporterVisitorImpl { + + public: + DoubleWaitVisitor(const clang::ento::MemRegion *const MemoryRegion, + const clang::mpi::MPIFunctionClassifier &FC) + : MR(MemoryRegion), FuncClassifier{FC} {} + + void Profile(llvm::FoldingSetNodeID &ID) const override { + //--------------- + // TODO what is this pattern used for? + static int X = 0; + ID.AddPointer(&X); + //--------------- + ID.AddPointer(MR); + } + + clang::ento::PathDiagnosticPiece * + VisitNode(const clang::ento::ExplodedNode *N, + const clang::ento::ExplodedNode *PrevN, + clang::ento::BugReporterContext &BRC, + clang::ento::BugReport &BR) override; + + private: + // The region tracked by analysis. + const clang::ento::MemRegion *const MR; + const clang::mpi::MPIFunctionClassifier &FuncClassifier; + }; +}; + +} // end of namespace: mpi +} // end of namespace: clang + +#endif Index: tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.cpp =================================================================== --- tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.cpp +++ tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.cpp @@ -0,0 +1,252 @@ +//===-- 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 +/// This file defines prefabricated reports which are emitted in +/// case of MPI related bugs, detected by AST-based and path-sensitive analysis. +/// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "MPIBugReporter.h" +#include "Utility.h" + +using namespace clang::ento; + +namespace clang { +namespace mpi { + +std::string MPIBugReporter::lineNumber(const CallEventRef<> CERef) const { + std::string LineNo = CERef->getSourceRange().getBegin().printToString( + BugReporter.getSourceManager()); + + // Split written string into parts. + std::vector Strs = util::split(LineNo, ':'); + return util::split(LineNo, ':').at(Strs.size() - 2); +} + +// bug reports –––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– + +// ast reports –––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– +void MPIBugReporter::reportTypeMismatch( + const CallExpr *MPICallExpr, const std::pair &IdxPair, + const std::string &BufferTypeName, std::string MPIType) const { + auto ADC = AnalysisManager.getAnalysisDeclContext(CurrentFunctionDecl); + PathDiagnosticLocation Location = PathDiagnosticLocation::createBegin( + MPICallExpr, BugReporter.getSourceManager(), ADC); + + SourceRange MPICallExprRange = MPICallExpr->getCallee()->getSourceRange(); + std::string BT{"Type mismatch"}; + std::string ErrorText{"Buffer type '" + BufferTypeName + + +"' and specified MPI type '" + MPIType + + "' do not match. "}; + + llvm::SmallVector SourceRanges; + SourceRanges.push_back(MPICallExprRange); + SourceRanges.push_back(MPICallExpr->getArg(IdxPair.first)->getSourceRange()); + SourceRanges.push_back(MPICallExpr->getArg(IdxPair.second)->getSourceRange()); + + BugReporter.EmitBasicReport(ADC->getDecl(), &CkrBase, BT, MPIError, ErrorText, + Location, SourceRanges); +} + +void MPIBugReporter::reportIncorrectBufferReferencing( + const CallExpr *MPICallExpr, size_t Idx, size_t PointerCount) const { + auto ADC = AnalysisManager.getAnalysisDeclContext(CurrentFunctionDecl); + PathDiagnosticLocation Location = PathDiagnosticLocation::createBegin( + MPICallExpr, BugReporter.getSourceManager(), ADC); + + SourceRange MPICallExprRange = MPICallExpr->getCallee()->getSourceRange(); + std::string BT{"Incorrect buffer referencing"}; + std::string ErrorText{ + "Buffer is not correctly dereferenced. It is passed as a " + + std::string(PointerCount, '*') + " pointer. "}; + + llvm::SmallVector SourceRanges; + SourceRanges.push_back(MPICallExprRange); + SourceRanges.push_back(MPICallExpr->getArg(Idx)->getSourceRange()); + + BugReporter.EmitBasicReport(ADC->getDecl(), &CkrBase, BT, MPIError, ErrorText, + Location, SourceRanges); +} + +void MPIBugReporter::reportInvalidArgumentType( + const CallExpr *const MPICallExpr, const size_t Idx) const { + auto ADC = AnalysisManager.getAnalysisDeclContext(CurrentFunctionDecl); + PathDiagnosticLocation Location = PathDiagnosticLocation::createBegin( + MPICallExpr, BugReporter.getSourceManager(), ADC); + + std::string IndexAsString{std::to_string(Idx)}; + SourceRange MPICallExprRange = MPICallExpr->getCallee()->getSourceRange(); + std::string BT{"Invalid argument type"}; + std::string ErrorText{"The type, argument at index " + IndexAsString + + " evaluates to, is not an integer type. "}; + + SmallVector SourceRanges; + SourceRanges.push_back(MPICallExprRange); + SourceRanges.push_back(MPICallExpr->getArg(Idx)->getSourceRange()); + BugReporter.EmitBasicReport(ADC->getDecl(), &CkrBase, BT, MPIError, ErrorText, + Location, SourceRanges); +} + +// path-sensitive reports ––––––––––––––––––––––––––––––––––––––––––––––––– +void MPIBugReporter::reportDoubleWait( + const CallEvent &MPICallExpr, const Request &Req, + const ExplodedNode *const ExplNode) const { + std::string LineNo{lineNumber(Req.LastUser)}; + // std::string LastUser = Req.LastUser->getCalleeIdentifier()->getName(); + std::string ErrorText{"Request '" + Req.variableName() + + // "' is already waited upon by '" + LastUser + + "' is already waited upon by call" + +" in line " + + LineNo + ". "}; + + auto Report = + llvm::make_unique(*DoubleWaitBugType, ErrorText, ExplNode); + + Report->addRange(MPICallExpr.getSourceRange()); + Report->addRange(Req.LastUser->getSourceRange()); + SourceRange Range = util::sourceRange(Req.MR); + if (Range.isValid()) + Report->addRange(Range); + + /////////// + Report->addVisitor( + llvm::make_unique(Req.MR, FuncClassifier)); + Report->markInteresting(Req.MR); + /////////// + + BugReporter.emitReport(std::move(Report)); +} + +PathDiagnosticPiece *MPIBugReporter::DoubleWaitVisitor::VisitNode( + const ExplodedNode *N, const ExplodedNode *PrevN, BugReporterContext &BRC, + BugReport &BR) { + + // Find out where the first wait appeared. + // Region must exist at both nodes. + const mpi::Request *Req = N->getState()->get(MR); + if (!Req) + return nullptr; + + // This is never reached... + llvm::outs() << "////////////////" + << "\n"; + + const mpi::Request *ReqPrev = PrevN->getState()->get(MR); + if (!ReqPrev) + return nullptr; + + // get both wait calls + const CallExpr *CEPrev = + cast(PrevN->getLocation().castAs().getStmt()); + + const CallExpr *CE = + cast(N->getLocation().castAs().getStmt()); + + // Calls should not be identical. + if (CEPrev == CE) { + return nullptr; + } + + // Check if both calls are wait operations. + if (!FuncClassifier.isWaitType(util::getIdentInfo(CEPrev)) || + !FuncClassifier.isWaitType(util::getIdentInfo(CE))) { + return nullptr; + } + + // TODO + // const FunctionDecl *funDecl = CE->getDirectCallee(); + // assert(funDecl && "We do not support indirect function calls as of now."); + // StringRef funName = funDecl->getName(); + + PathDiagnosticLocation Pos(CE, BRC.getSourceManager(), + N->getLocationContext()); + return new PathDiagnosticEventPiece(Pos, "Request was previously used here."); +} + +void MPIBugReporter::reportDoubleNonblocking( + const CallEvent &MPICallExpr, const Request &Req, + const ExplodedNode *const ExplNode) const { + std::string LineNo{lineNumber(Req.LastUser)}; + // std::string LastUser = Req.LastUser->getCalleeIdentifier()->getName(); + + std::string ErrorText{ + "Request '" + Req.variableName() + + //"' is already in use by nonblocking call '" + LastUser + + "' is already in use by nonblocking call '" + "' in line " + + LineNo + ". "}; + + auto Report = llvm::make_unique(*DoubleNonblockingBugType, + ErrorText, ExplNode); + Report->addRange(MPICallExpr.getSourceRange()); + Report->addRange(Req.LastUser->getSourceRange()); + SourceRange Range = util::sourceRange(Req.MR); + if (Range.isValid()) + Report->addRange(Range); + + ///////////// + // Report->addVisitor(llvm::make_unique(Req.MR)); + // Report->markInteresting(Req.MR); + ///////////// + + BugReporter.emitReport(std::move(Report)); +} + +void MPIBugReporter::reportMissingWait( + const Request &Req, const ExplodedNode *const ExplNode) const { + + std::string LineNo{lineNumber(Req.LastUser)}; + // std::string LastUser = Req.LastUser->getCalleeIdentifier()->getName(); + + std::string ErrorText{ + // "'" + LastUser + "' in line " + LineNo + ", using request '" + + "Call in line " + LineNo + ", using request '" + Req.variableName() + + "', has no matching wait in the scope of this function. "}; + + auto Report = + llvm::make_unique(*MissingWaitBugType, ErrorText, ExplNode); + + Report->addRange(Req.LastUser->getSourceRange()); + SourceRange Range = util::sourceRange(Req.MR); + if (Range.isValid()) + Report->addRange(Range); + + ///////////// + // Report->addVisitor(llvm::make_unique(Req.MR)); + // Report->markInteresting(Req.MR); + ///////////// + + BugReporter.emitReport(std::move(Report)); +} + +void MPIBugReporter::reportUnmatchedWait( + const CallEvent &CE, const clang::ento::MemRegion *const ReqRegion, + const ExplodedNode *const ExplNode) const { + std::string ErrorText{"Request '" + util::variableName(ReqRegion) + + "' has no matching nonblocking call. "}; + + auto Report = + llvm::make_unique(*UnmatchedWaitBugType, ErrorText, ExplNode); + + Report->addRange(CE.getSourceRange()); + SourceRange Range = util::sourceRange(ReqRegion); + if (Range.isValid()) + Report->addRange(Range); + + ///////////// + // Report->addVisitor(llvm::make_unique(ReqRegion)); + // Report->markInteresting(ReqRegion); + ///////////// + + BugReporter.emitReport(std::move(Report)); +} + +} // end of namespace: mpi +} // end of namespace: clang Index: tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.cpp =================================================================== --- tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.cpp +++ tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.cpp @@ -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 +/// This file defines the main class of MPI-Checker which serves as an entry +/// point. It is created once for each translation unit analysed. +/// +//===----------------------------------------------------------------------===// + +#include "../ClangSACheckers.h" +#include "TranslationUnitVisitor.h" +#include "MPICheckerPathSensitive.h" + +using namespace clang::ento; + +namespace clang { +namespace mpi { +class MPIChecker : public Checker, + check::PreCall, check::EndFunction> { +public: + // ast callback––––––––––––––––––––––––––––––––––––––––––––––––––––––– + void checkASTDecl(const TranslationUnitDecl *TuDecl, AnalysisManager &AM, + BugReporter &BR) const { + + // traverse translation unit + TranslationUnitVisitor TuVisitor{BR, *this, AM}; + TuVisitor.TraverseTranslationUnitDecl( + const_cast(TuDecl)); + } + + // path-sensitive callbacks–––––––––––––––––––––––––––––––––––––––––––– + void checkPreCall(const CallEvent &CE, CheckerContext &Ctx) const { + dynamicInit(Ctx); + CheckerSens->checkWaitUsage(CE, Ctx); + CheckerSens->checkDoubleNonblocking(CE, 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 CheckerSens; + + void dynamicInit(CheckerContext &Ctx) const { + if (!CheckerSens) { + const_cast &>(CheckerSens) + .reset(new MPICheckerPathSensitive(Ctx.getAnalysisManager(), this, + Ctx.getBugReporter())); + } + } +}; + +} // end of namespace: mpi +} // end of namespace: clang + +// registers the checker for static analysis. +void clang::ento::registerMPIChecker(CheckerManager &MGR) { + MGR.registerChecker(); +} Index: tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPICheckerAST.h =================================================================== --- tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPICheckerAST.h +++ tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPICheckerAST.h @@ -0,0 +1,110 @@ +//===-- MPICheckerAST.h - 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 +/// This file defines AST-based checks, to verify correct usage of the MPI API. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MPICHECKER_MPICHECKERAST_H +#define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MPICHECKER_MPICHECKERAST_H + +#include "MPIFunctionClassifier.h" +#include "MPIBugReporter.h" +#include "Container.h" +#include "Utility.h" +#include "TypeVisitor.h" + +namespace clang { +namespace mpi { + +class MPICheckerAST { +public: + MPICheckerAST(clang::ento::BugReporter &BR, + const clang::ento::CheckerBase &CB, + clang::ento::AnalysisManager &AM) + : FuncClassifier{AM}, BugReporter{BR, CB, AM}, AnalysisManager{AM} { + initMPITypeContainer(); + } + + using IndexPairs = llvm::SmallVector, 2>; + + /// Checks if expressions evaluate to a non-integer type at indices where only + /// integer values are valid (count, rank, tag). + /// + /// \param MPICallExpr call to check the arguments for + void checkForInvalidArgs(const clang::CallExpr *const MPICallExpr) const; + + /// Checks if buffer type and specified MPI datatype matches. + /// \param MPICallExpr call to check type correspondence for + void checkBufferTypeMatch(const clang::CallExpr *const MPICallExpr) const; + + /// Set function currently visited to pass the information to the bug + /// reporter, in case of a found bug. + /// + /// \param FuncDecl current function visited + void setCurrentlyVisitedFunction(const clang::FunctionDecl *const FuncDecl); + +private: + /// Init MPI type container to recognize all MPI types defined by the + /// MPI standard. (MPI_BYTE, MPI_PACKED are excluded) + void initMPITypeContainer(); + + /// Returns index pairs for each buffer, datatype pair. + /// + /// \param MPICallExpr MPI call to get the buffer, MPI datatype pairs for + IndexPairs + bufferDataTypeIndices(const clang::CallExpr *const MPICallExpr) const; + + /// Return an array of indices that must be of integer type for a given + /// call. + /// + /// \param MPICallExpr MPI call to get the indices for + llvm::SmallVector + integerIndices(const clang::CallExpr *const MPICallExpr) const; + + /// Selects an appropriate function to match the buffer type against the + /// specified MPI datatype. + /// + /// \param TypeVis contains information about the buffer type + /// \param IsBufferTypeNull if the buffer is a null pointer type + /// \param MPICallExpr buffer index pair is observed + /// \param MPIDatatypeString MPI datatype as string + /// \param IdxPair buffer, MPI datatype index pair + void selectTypeMatcher(const mpi::TypeVisitor &TypeVis, bool IsBufferTypeNull, + const clang::CallExpr *const MPICallExpr, + const llvm::StringRef MPIDatatypeString, + const std::pair &IdxPair) const; + bool matchNullPtrType(const llvm::StringRef MPIDatatype) const; + bool matchBoolType(const llvm::StringRef MPIDatatype) const; + bool matchCharType(const mpi::TypeVisitor &TypeVis, + const llvm::StringRef MPIDatatype) const; + bool matchSignedType(const mpi::TypeVisitor &TypeVis, + const llvm::StringRef MPIDatatype) const; + bool matchUnsignedType(const mpi::TypeVisitor &TypeVis, + const llvm::StringRef MPIDatatype) const; + bool matchFloatType(const mpi::TypeVisitor &TypeVis, + const llvm::StringRef MPIDatatype) const; + bool matchComplexCType(const mpi::TypeVisitor &TypeVis, + const llvm::StringRef MPIDatatype) const; + bool matchComplexCPPType(const mpi::TypeVisitor &TypeVis, + const llvm::StringRef MPIDatatype) const; + bool matchExactWidthType(const mpi::TypeVisitor &TypeVis, + const llvm::StringRef MPIDatatype) const; + + const MPIFunctionClassifier FuncClassifier; + llvm::SmallVector MPITypes; + MPIBugReporter BugReporter; + clang::ento::AnalysisManager &AnalysisManager; +}; + +} // end of namespace: mpi +} // end of namespace: clang + +#endif Index: tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPICheckerAST.cpp =================================================================== --- tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPICheckerAST.cpp +++ tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPICheckerAST.cpp @@ -0,0 +1,397 @@ +//===-- 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 +/// This file defines AST-based checks, to verify correct usage of the MPI API. +/// +//===----------------------------------------------------------------------===// + +#include "MPICheckerAST.h" + +namespace clang { +namespace mpi { + +void MPICheckerAST::initMPITypeContainer() { + MPITypes = {"MPI_DATATYPE_NULL", + "MPI_C_BOOL", + "MPI_CHAR", + "MPI_SIGNED_CHAR", + "MPI_UNSIGNED_CHAR", + "MPI_WCHAR", + "MPI_INT", + "MPI_LONG", + "MPI_SHORT", + "MPI_LONG_LONG", + "MPI_LONG_LONG_INT", + "MPI_UNSIGNED", + "MPI_UNSIGNED_SHORT", + "MPI_UNSIGNED_LONG", + "MPI_UNSIGNED_LONG_LONG", + "MPI_FLOAT", + "MPI_DOUBLE", + "MPI_LONG_DOUBLE", + "MPI_C_COMPLEX", + "MPI_C_FLOAT_COMPLEX", + "MPI_C_DOUBLE_COMPLEX", + "MPI_C_LONG_DOUBLE_COMPLEX", + "MPI_INT8_T", + "MPI_INT16_T", + "MPI_INT32_T", + "MPI_INT64_T", + "MPI_UINT8_T", + "MPI_UINT16_T", + "MPI_UINT32_T", + "MPI_UINT64_T", + "MPI_CXX_BOOL", + "MPI_CXX_FLOAT_COMPLEX", + "MPI_CXX_DOUBLE_COMPLEX", + "MPI_CXX_LONG_DOUBLE_COMPLEX"}; +} + +void MPICheckerAST::checkBufferTypeMatch( + const clang::CallExpr *const MPICallExpr) const { + // One pair consists of a {bufferIdx, MPIDatatypeIdx}. + IndexPairs IdxPairs = bufferDataTypeIndices(MPICallExpr); + + // For every buffer, MPI pair in function, check if their types match. + for (const auto &IdxPair : IdxPairs) { + auto BufferType = + MPICallExpr->getArg(IdxPair.first)->IgnoreImpCasts()->getType(); + + // Collect buffer type information. + const mpi::TypeVisitor TypeVis{BufferType}; + + // Get MPI data type as string. + StringRef MPIDatatypeString{util::sourceRangeAsStringRef( + MPICallExpr->getArg(IdxPair.second)->getSourceRange(), + AnalysisManager)}; + + // Detect null pointer constant type. + const bool IsBufferTypeNull = + MPICallExpr->getArg(IdxPair.first) + ->isNullPointerConstant(AnalysisManager.getASTContext(), + Expr::NPC_ValueDependentIsNull); + + // Check if the buffer is correctly referenced. + if (TypeVis.pointerCount() != 1 && !IsBufferTypeNull) { + BugReporter.reportIncorrectBufferReferencing(MPICallExpr, IdxPair.first, + TypeVis.pointerCount()); + } + + // if MPI type not known + if (!cont::contains(MPITypes, MPIDatatypeString)) + return; + + selectTypeMatcher(TypeVis, IsBufferTypeNull, MPICallExpr, MPIDatatypeString, + IdxPair); + } +} + +MPICheckerAST::IndexPairs MPICheckerAST::bufferDataTypeIndices( + const clang::CallExpr *const MPICallExpr) const { + IndexPairs IdxPairs; + + const IdentifierInfo *const Ident = util::getIdentInfo(MPICallExpr); + + if (FuncClassifier.isPointToPointType(Ident)) { + IdxPairs.push_back({MPIPointToPoint::Buf, MPIPointToPoint::Datatype}); + } else if (FuncClassifier.isCollectiveType(Ident)) { + if (FuncClassifier.isReduceType(Ident)) { + // Only check buffer type if is not inplace. + if (util::sourceRangeAsStringRef(MPICallExpr->getArg(0)->getSourceRange(), + AnalysisManager) != "MPI_IN_PLACE") { + IdxPairs.push_back({0, 3}); + } + IdxPairs.push_back({1, 3}); + } else if (FuncClassifier.isScatterType(Ident) || + FuncClassifier.isGatherType(Ident) || + FuncClassifier.isAlltoallType(Ident)) { + IdxPairs.push_back({0, 2}); + IdxPairs.push_back({3, 5}); + } else if (FuncClassifier.isBcastType(Ident)) { + IdxPairs.push_back({0, 2}); + } + } + return IdxPairs; +} + +void MPICheckerAST::selectTypeMatcher( + const mpi::TypeVisitor &TypeVis, const bool IsBufferTypeNull, + const clang::CallExpr *const MPICallExpr, const StringRef MPIDatatypeString, + const std::pair &IdxPair) const { + const clang::BuiltinType *BuiltinTy = TypeVis.builtinType(); + bool IsTypeMatching{true}; + + if (IsBufferTypeNull) { + IsTypeMatching = matchNullPtrType(MPIDatatypeString); + } else if (TypeVis.isTypedefType()) { + IsTypeMatching = matchExactWidthType(TypeVis, MPIDatatypeString); + } + // Check for complex-floating C types (e.g. float _Complex). + else if (TypeVis.isComplexCType()) { + IsTypeMatching = matchComplexCType(TypeVis, MPIDatatypeString); + } + // Check for complex-floating CPP types (e.g. std::complex). + else if (TypeVis.templateType() && TypeVis.templateName() == "complex") { + IsTypeMatching = matchComplexCPPType(TypeVis, MPIDatatypeString); + } + // Check for basic builtin types (e.g. int, char). + else if (!BuiltinTy) { + return; // if no builtin type cancel checking + } else if (BuiltinTy->isBooleanType()) { + IsTypeMatching = matchBoolType(MPIDatatypeString); + } else if (BuiltinTy->isAnyCharacterType()) { + IsTypeMatching = matchCharType(TypeVis, MPIDatatypeString); + } else if (BuiltinTy->isSignedInteger()) { + IsTypeMatching = matchSignedType(TypeVis, MPIDatatypeString); + } else if (BuiltinTy->isUnsignedIntegerType()) { + IsTypeMatching = matchUnsignedType(TypeVis, MPIDatatypeString); + } else if (BuiltinTy->isFloatingType()) { + IsTypeMatching = matchFloatType(TypeVis, MPIDatatypeString); + } + + if (!IsTypeMatching) { + std::string BufferTypeName; + if (!IsBufferTypeNull) { + BufferTypeName = TypeVis.derefUnqualType().getAsString(); + } else { + BufferTypeName = "null pointer"; + } + BugReporter.reportTypeMismatch(MPICallExpr, IdxPair, BufferTypeName, + MPIDatatypeString); + } +} + +bool MPICheckerAST::matchNullPtrType(const llvm::StringRef MPIDatatype) const { + return (MPIDatatype == "MPI_DATATYPE_NULL"); +} + +bool MPICheckerAST::matchBoolType(const llvm::StringRef MPIDatatype) const { + return (MPIDatatype == "MPI_C_BOOL" || MPIDatatype == "MPI_CXX_BOOL"); +} + +bool MPICheckerAST::matchCharType(const mpi::TypeVisitor &TypeVis, + const llvm::StringRef MPIDatatype) const { + bool IsTypeMatching; + switch (TypeVis.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 &TypeVis, + const llvm::StringRef MPIDatatype) const { + bool IsTypeMatching; + + switch (TypeVis.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 &TypeVis, + const llvm::StringRef MPIDatatype) const { + bool IsTypeMatching; + + switch (TypeVis.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 &TypeVis, + const llvm::StringRef MPIDatatype) const { + bool IsTypeMatching; + + switch (TypeVis.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::matchComplexCType(const mpi::TypeVisitor &TypeVis, + const llvm::StringRef MPIDatatype) const { + bool IsTypeMatching; + + switch (TypeVis.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::matchComplexCPPType( + const mpi::TypeVisitor &TypeVis, const llvm::StringRef MPIDatatype) const { + bool IsTypeMatching; + + switch (TypeVis.builtinType()->getKind()) { + case BuiltinType::Float: + IsTypeMatching = (MPIDatatype == "MPI_CXX_FLOAT_COMPLEX"); + break; + case BuiltinType::Double: + IsTypeMatching = (MPIDatatype == "MPI_CXX_DOUBLE_COMPLEX"); + break; + case BuiltinType::LongDouble: + IsTypeMatching = (MPIDatatype == "MPI_CXX_LONG_DOUBLE_COMPLEX"); + break; + default: + IsTypeMatching = true; + } + + return IsTypeMatching; +} + +bool MPICheckerAST::matchExactWidthType( + const mpi::TypeVisitor &TypeVis, const llvm::StringRef MPIDatatype) const { + // Check typedef type match. + // No break needs to be specified for string switch. + bool IsTypeMatching = llvm::StringSwitch(TypeVis.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 MPICallExpr) const { + llvm::SmallVector IndicesToCheck{integerIndices(MPICallExpr)}; + if (!IndicesToCheck.size()) + return; + + // Iterate indices which should have integer arguments. + for (const size_t Idx : IndicesToCheck) { + if (!MPICallExpr->getArg(Idx) + ->IgnoreImpCasts() + ->getType() + ->isIntegerType()) { + BugReporter.reportInvalidArgumentType(MPICallExpr, Idx); + } + } +} + +llvm::SmallVector +MPICheckerAST::integerIndices(const clang::CallExpr *const MPICallExpr) const { + llvm::SmallVector IntIndices; + + const IdentifierInfo *const Ident = util::getIdentInfo(MPICallExpr); + + 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 FuncDecl) { + BugReporter.CurrentFunctionDecl = FuncDecl; +} + +} // end of namespace: mpi +} // end of namespace: clang Index: tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPICheckerPathSensitive.h =================================================================== --- tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPICheckerPathSensitive.h +++ tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPICheckerPathSensitive.h @@ -0,0 +1,88 @@ +//===-- MPICheckerPathSensitive.h - 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 +/// This file defines path-sensitive checks, to verify correct usage of the +/// MPI API. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MPICHECKER_MPICHECKERPATHSENSITIVE_H +#define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MPICHECKER_MPICHECKERPATHSENSITIVE_H + +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "MPIFunctionClassifier.h" +#include "MPITypes.h" +#include "MPIBugReporter.h" + +namespace clang { +namespace mpi { + +class MPICheckerPathSensitive { +public: + MPICheckerPathSensitive(clang::ento::AnalysisManager &AM, + const clang::ento::CheckerBase *CB, + clang::ento::BugReporter &BR) + : FuncClassifier{AM}, BugReporter{BR, *CB, AM} {} + + /// Checks if a request is used by nonblocking calls multiple times + /// in sequence without intermediate wait. The check contains a guard, + /// in order to only inspect nonblocking functions. + /// + /// \param PreCallEvent MPI call to verify + void checkDoubleNonblocking(const clang::ento::CallEvent &PreCallEvent, + clang::ento::CheckerContext &Ctx) const; + + /// Checks if a request is used by a wait multiple times in sequence without + /// intermediate nonblocking call or if the request used by the wait + /// function was not used at all before. The check contains a guard, + /// in order to only inspect wait functions. + /// + /// \param PreCallEvent MPI call to verify + void checkWaitUsage(const clang::ento::CallEvent &PreCallEvent, + clang::ento::CheckerContext &Ctx) const; + + /// Check if a nonblocking call has no matching wait. + /// When this function is invoked, any requests whose last user + /// is a nonblocking call are rated as missing waits. + void checkMissingWaits(clang::ento::CheckerContext &Ctx); + + /// Erase all requests from the path-sensitive map. + void clearRequests(clang::ento::CheckerContext &Ctx) const; + +private: + /// Returns the memory region used by a wait function. + /// Distinguishes between MPI_Wait and MPI_Waitall. + /// + /// \param CE MPI wait call + const clang::ento::MemRegion * + memRegionUsedInWait(const clang::ento::CallEvent &CE) const; + + /// Collects all memory regions of a request(array) used by 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 ReqRegions vector the regions get pushed into + /// \param MR top most region to iterate + /// \param CE MPI wait call using the request(s) + void collectUsedMemRegions( + llvm::SmallVector &ReqRegions, + const clang::ento::MemRegion *MR, const clang::ento::CallEvent &CE, + clang::ento::CheckerContext &Ctx) const; + + const MPIFunctionClassifier FuncClassifier; + MPIBugReporter BugReporter; +}; + +} // end of namespace: mpi +} // end of namespace: clang + +#endif Index: tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPICheckerPathSensitive.cpp =================================================================== --- tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPICheckerPathSensitive.cpp +++ tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPICheckerPathSensitive.cpp @@ -0,0 +1,163 @@ +//===-- 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 +/// This file defines path-sensitive checks, to verify correct usage of the +/// MPI API. +/// +//===----------------------------------------------------------------------===// + +#include "MPICheckerPathSensitive.h" +#include "Utility.h" + +namespace clang { +namespace mpi { + +using namespace clang::ento; + +void MPICheckerPathSensitive::checkDoubleNonblocking( + const clang::ento::CallEvent &PreCallEvent, CheckerContext &Ctx) const { + if (!FuncClassifier.isNonBlockingType(PreCallEvent.getCalleeIdentifier())) { + return; + } + const MemRegion *MR = + PreCallEvent.getArgSVal(PreCallEvent.getNumArgs() - 1).getAsRegion(); + + // There is no way to reason about a symbolic region. + if (MR->getBaseRegion()->getAs()) + return; + + ProgramStateRef State = Ctx.getState(); + CallEventRef<> CERef = PreCallEvent.cloneWithState(State); + + const Request *Req = State->get(MR); + const ExplodedNode *const ExplNode = Ctx.addTransition(); + + if (Req) { + if (FuncClassifier.isNonBlockingType( + Req->LastUser->getCalleeIdentifier())) { + BugReporter.reportDoubleNonblocking(PreCallEvent, *Req, ExplNode); + } + } + + State = State->set(MR, mpi::Request{MR, CERef}); + Ctx.addTransition(State); +} + +void MPICheckerPathSensitive::checkWaitUsage( + const clang::ento::CallEvent &PreCallEvent, CheckerContext &Ctx) const { + if (!FuncClassifier.isWaitType(PreCallEvent.getCalleeIdentifier())) + return; + const MemRegion *MR = memRegionUsedInWait(PreCallEvent); + if (!MR) + return; + + // There is no way to reason about a symbolic region. + if (MR->getBaseRegion()->getAs()) + return; + + ProgramStateRef State = Ctx.getState(); + CallEventRef<> CERef = PreCallEvent.cloneWithState(State); + const ExplodedNode *const ExplNode = Ctx.addTransition(); + llvm::SmallVector ReqRegions; + collectUsedMemRegions(ReqRegions, MR, PreCallEvent, Ctx); + + // Check all request regions used by the wait function. + for (const auto ReqRegion : ReqRegions) { + const Request *Req = State->get(ReqRegion); + State = State->set(ReqRegion, {ReqRegion, CERef}); + if (Req) { + // Check for double wait. + if (FuncClassifier.isWaitType(Req->LastUser->getCalleeIdentifier())) { + BugReporter.reportDoubleWait(PreCallEvent, *Req, ExplNode); + } + } + // A wait has no matching nonblocking call. + else { + BugReporter.reportUnmatchedWait(PreCallEvent, ReqRegion, ExplNode); + } + } + + Ctx.addTransition(State); +} + +void MPICheckerPathSensitive::checkMissingWaits(CheckerContext &Ctx) { + ProgramStateRef State = Ctx.getState(); + auto Requests = State->get(); + ExplodedNode *ExplNode = Ctx.addTransition(); + // At the end of a function, immediate calls should be matched with a wait. + for (auto &Req : Requests) { + if (Req.second.LastUser && + FuncClassifier.isNonBlockingType( + Req.second.LastUser->getCalleeIdentifier())) { + BugReporter.reportMissingWait(Req.second, ExplNode); + } + } +} + +void MPICheckerPathSensitive::clearRequests(CheckerContext &Ctx) const { + ProgramStateRef State = Ctx.getState(); + auto Requests = State->get(); + // Clear rank container. + for (auto &Req : Requests) { + State = State->remove(Req.first); + } + Ctx.addTransition(State); +} + +const MemRegion *MPICheckerPathSensitive::memRegionUsedInWait( + const clang::ento::CallEvent &CE) const { + if (FuncClassifier.isMPI_Wait(CE.getCalleeIdentifier())) { + return CE.getArgSVal(0).getAsRegion(); + } else if (FuncClassifier.isMPI_Waitall(CE.getCalleeIdentifier())) { + return CE.getArgSVal(1).getAsRegion(); + } else { + return (const MemRegion *)nullptr; + } +} + +void MPICheckerPathSensitive::collectUsedMemRegions( + llvm::SmallVector &ReqRegions, const MemRegion *MR, + const clang::ento::CallEvent &CE, CheckerContext &Ctx) const { + ProgramStateRef State = Ctx.getState(); + MemRegionManager *RegionManager = MR->getMemRegionManager(); + + if (FuncClassifier.isMPI_Waitall(CE.getCalleeIdentifier())) { + const MemRegion *SuperRegion{nullptr}; + if (const ElementRegion *ER = MR->getAs()) { + SuperRegion = ER->getSuperRegion(); + } + + // A single request is passed to MPI_Waitall. + if (!SuperRegion) { + ReqRegions.push_back(MR); + return; + } + + auto size = Ctx.getStoreManager().getSizeInElements( + State, SuperRegion, CE.getArgExpr(1)->getType()->getPointeeType()); + + const llvm::APSInt &arrSize = size.getAs()->getValue(); + + for (size_t i = 0; i < arrSize; ++i) { + NonLoc Idx = Ctx.getSValBuilder().makeArrayIndex(i); + + const ElementRegion *ER = RegionManager->getElementRegion( + CE.getArgExpr(1)->getType()->getPointeeType(), Idx, SuperRegion, + Ctx.getASTContext()); + + ReqRegions.push_back(ER->getAs()); + } + } else if (FuncClassifier.isMPI_Wait(CE.getCalleeIdentifier())) { + ReqRegions.push_back(MR); + } +} + +} // end of namespace: mpi +} // end of namespace: clang Index: tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIFunctionClassifier.h =================================================================== --- tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIFunctionClassifier.h +++ tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIFunctionClassifier.h @@ -0,0 +1,110 @@ +//===-- MPIFunctionClassifier.h - 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 +/// This file defines functionality to identify and classify MPI functions. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MPICHECKER_MPIFUNCTIONCLASSIFIER_H +#define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MPICHECKER_MPIFUNCTIONCLASSIFIER_H + +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" + +namespace clang { +namespace mpi { + +class MPIFunctionClassifier { +public: + MPIFunctionClassifier(clang::ento::AnalysisManager &AM) { + identifierInit(AM); + } + + // general identifiers––––––––––––––––––––––––––––––––––––––––––––––––– + bool isMPIType(const clang::IdentifierInfo *const IdentInfo) const; + bool isBlockingType(const clang::IdentifierInfo *const IdentInfo) const; + bool isNonBlockingType(const clang::IdentifierInfo *const IdentInfo) const; + + // point-to-point identifiers–––––––––––––––––––––––––––––––––––––––––– + bool isPointToPointType(const clang::IdentifierInfo *const IdentInfo) const; + bool isSendType(const clang::IdentifierInfo *const IdentInfo) const; + bool isRecvType(const clang::IdentifierInfo *const IdentInfo) const; + + // collective identifiers–––––––––––––––––––––––––––––––––––––––––––––– + bool isCollectiveType(const clang::IdentifierInfo *const IdentInfo) const; + bool isCollToColl(const clang::IdentifierInfo *const IdentInfo) const; + bool isScatterType(const clang::IdentifierInfo *const IdentInfo) const; + bool isGatherType(const clang::IdentifierInfo *const IdentInfo) const; + bool isAllgatherType(const clang::IdentifierInfo *const IdentInfo) const; + bool isAlltoallType(const clang::IdentifierInfo *const IdentInfo) const; + bool isReduceType(const clang::IdentifierInfo *const IdentInfo) const; + bool isBcastType(const clang::IdentifierInfo *const IdentInfo) const; + + // additional identifiers –––––––––––––––––––––––––––––––––––––––––––––– + bool isMPI_Comm_rank(const clang::IdentifierInfo *const IdentInfo) const; + bool isMPI_Comm_size(const clang::IdentifierInfo *const IdentInfo) const; + bool isMPI_Wait(const clang::IdentifierInfo *const IdentInfo) const; + bool isMPI_Waitall(const clang::IdentifierInfo *const IdentInfo) const; + bool isMPI_Waitany(const clang::IdentifierInfo *const IdentInfo) const; + bool isMPI_Waitsome(const clang::IdentifierInfo *const IdentInfo) const; + bool isWaitType(const clang::IdentifierInfo *const IdentInfo) const; + +private: + // Initializes function identifiers, to recognize them during analysis. + void identifierInit(clang::ento::AnalysisManager &AM); + void initPointToPointIdentifiers(clang::ento::AnalysisManager &AM); + void initCollectiveIdentifiers(clang::ento::AnalysisManager &AM); + void initAdditionalIdentifiers(clang::ento::AnalysisManager &AM); + + // The containers are used, to enable classification of MPI-functions during + // analysis. + llvm::SmallVector MPISendTypes; + llvm::SmallVector MPIRecvTypes; + + llvm::SmallVector MPIBlockingTypes; + llvm::SmallVector MPINonBlockingTypes; + + llvm::SmallVector MPIPointToPointTypes; + llvm::SmallVector MPICollectiveTypes; + + llvm::SmallVector MPIPointToCollTypes; + llvm::SmallVector MPICollToPointTypes; + llvm::SmallVector MPICollToCollTypes; + + llvm::SmallVector 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 +} // end of namespace: clang + +#endif Index: tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIFunctionClassifier.cpp =================================================================== --- tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIFunctionClassifier.cpp +++ tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIFunctionClassifier.cpp @@ -0,0 +1,359 @@ +//===-- 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 +/// This file defines functionality to identify and classify MPI functions. +/// +//===----------------------------------------------------------------------===// + +#include "MPIFunctionClassifier.h" +#include "Utility.h" +#include "Container.h" + +namespace clang { +namespace mpi { + +void MPIFunctionClassifier::identifierInit(clang::ento::AnalysisManager &AM) { + // Initialize function identifiers. + initPointToPointIdentifiers(AM); + initCollectiveIdentifiers(AM); + initAdditionalIdentifiers(AM); +} + +void MPIFunctionClassifier::initPointToPointIdentifiers( + clang::ento::AnalysisManager &AM) { + ASTContext &ASTCtx = AM.getASTContext(); + + // Copy identifiers into the correct classification containers. + IdentInfo_MPI_Send = &ASTCtx.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 = &ASTCtx.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 = &ASTCtx.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 = &ASTCtx.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 = &ASTCtx.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 = &ASTCtx.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 = &ASTCtx.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 = &ASTCtx.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 = &ASTCtx.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 = &ASTCtx.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 &AM) { + ASTContext &ASTCtx = AM.getASTContext(); + + // Copy identifiers into the correct classification containers. + IdentInfo_MPI_Scatter = &ASTCtx.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 = &ASTCtx.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 = &ASTCtx.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 = &ASTCtx.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 = &ASTCtx.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 = &ASTCtx.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 = &ASTCtx.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 = &ASTCtx.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 = &ASTCtx.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 = &ASTCtx.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 = &ASTCtx.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 = &ASTCtx.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 = &ASTCtx.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 = &ASTCtx.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 &AM) { + ASTContext &ASTCtx = AM.getASTContext(); + + IdentInfo_MPI_Comm_rank = &ASTCtx.Idents.get("MPI_Comm_rank"); + MPIType.push_back(IdentInfo_MPI_Comm_rank); + assert(IdentInfo_MPI_Comm_rank); + + IdentInfo_MPI_Comm_size = &ASTCtx.Idents.get("MPI_Comm_size"); + MPIType.push_back(IdentInfo_MPI_Comm_size); + assert(IdentInfo_MPI_Comm_size); + + IdentInfo_MPI_Wait = &ASTCtx.Idents.get("MPI_Wait"); + MPIType.push_back(IdentInfo_MPI_Wait); + assert(IdentInfo_MPI_Wait); + + IdentInfo_MPI_Waitall = &ASTCtx.Idents.get("MPI_Waitall"); + MPIType.push_back(IdentInfo_MPI_Waitall); + assert(IdentInfo_MPI_Waitall); + + IdentInfo_MPI_Waitany = &ASTCtx.Idents.get("MPI_Waitany"); + MPIType.push_back(IdentInfo_MPI_Waitany); + assert(IdentInfo_MPI_Waitany); + + IdentInfo_MPI_Waitsome = &ASTCtx.Idents.get("MPI_Waitsome"); + MPIType.push_back(IdentInfo_MPI_Waitsome); + assert(IdentInfo_MPI_Waitsome); + + IdentInfo_MPI_Barrier = &ASTCtx.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::contains(MPIType, IdentInfo); +} + +bool MPIFunctionClassifier::isBlockingType( + const IdentifierInfo *IdentInfo) const { + return cont::contains(MPIBlockingTypes, IdentInfo); +} + +bool MPIFunctionClassifier::isNonBlockingType( + const IdentifierInfo *IdentInfo) const { + return cont::contains(MPINonBlockingTypes, IdentInfo); +} + +// point-to-point identifiers–––––––––––––––––––––––––––––––––––––––––– +bool MPIFunctionClassifier::isPointToPointType( + const IdentifierInfo *IdentInfo) const { + return cont::contains(MPIPointToPointTypes, IdentInfo); +} + +bool MPIFunctionClassifier::isSendType(const IdentifierInfo *IdentInfo) const { + return cont::contains(MPISendTypes, IdentInfo); +} + +bool MPIFunctionClassifier::isRecvType(const IdentifierInfo *IdentInfo) const { + return cont::contains(MPIRecvTypes, IdentInfo); +} + +// collective identifiers–––––––––––––––––––––––––––––––––––––––––––––– +bool MPIFunctionClassifier::isCollectiveType( + const IdentifierInfo *IdentInfo) const { + return cont::contains(MPICollectiveTypes, IdentInfo); +} + +bool MPIFunctionClassifier::isCollToColl( + const IdentifierInfo *IdentInfo) const { + return cont::contains(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 +} // end of namespace: clang Index: tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPITypes.h =================================================================== --- tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPITypes.h +++ tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPITypes.h @@ -0,0 +1,76 @@ +//===-- 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 +/// This file provides definitions, to model the schema of MPI point-to-point +/// functions and MPI requests. The mpi::Request class defines a wrapper +/// class, in order to make MPI requests trackable for path-sensitive analysis. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MPICHECKER_MPITYPES_H +#define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MPICHECKER_MPITYPES_H + +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "llvm/ADT/SmallSet.h" +#include "MPIFunctionClassifier.h" +#include "Utility.h" +#include "Container.h" + +namespace clang { +namespace mpi { +// argument schema enums –––––––––––––––––––––––––––––––––––––––––––––––––––– +// Scope enums but keep weak typing, to make values usable as indices. + +// Represents argument schema for MPI point-to-point functions. +// The existence and type of last argument must be checked dynamically. +namespace MPIPointToPoint { +enum { Buf, Count, Datatype, Rank, Tag, Comm, Request }; +} + +// for path-sensitive analysis––––––––––––––––––––––––––––––––––––––––––––––– +class Request { +public: + Request(const clang::ento::MemRegion *const MR, + const clang::ento::CallEventRef<> MPICallEvent) + : MR{MR}, LastUser{MPICallEvent} { + VariableName = util::variableName(MR); + } + + void Profile(llvm::FoldingSetNodeID &Id) const { + Id.AddPointer(MR); + Id.AddPointer(LastUser->getOriginExpr()); + } + + bool operator==(const Request &ToCompare) const { return ToCompare.MR == MR; } + + const clang::ento::MemRegion *const MR; + const clang::ento::CallEventRef<> LastUser; + + std::string variableName() const { return VariableName; } + +private: + std::string VariableName; +}; + +} // end of namespace: mpi +} // end of namespace: clang + +// Register data structure for path-sensitive analysis. The map stores MPI +// requests which are identified by their memory region. Requests are used in +// MPI to complete nonblocking operations with wait operations. Requests can be +// used as part of an array. Each MPI request is captured with the last +// nonblocking or wait function which used the request. If a request was never +// used by a nonblocking or wait function, it is not contained in the map and +// therefore regarded as unused. The other two possible states are: The request +// is in use by a nonblocking function; Or it is in use by a wait function. +REGISTER_MAP_WITH_PROGRAMSTATE(RequestMap, const clang::ento::MemRegion *, + clang::mpi::Request) + +#endif Index: tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/TranslationUnitVisitor.h =================================================================== --- tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/TranslationUnitVisitor.h +++ tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/TranslationUnitVisitor.h @@ -0,0 +1,45 @@ +//===-- TranslationUnitVisitor.h - 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 +/// This file defines a translation unit visitor class that invokes +/// AST-based checks on an MPICheckerAST instance during traversal. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MPICHECKER_TRANSLATIONUNITVISITOR_H +#define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MPICHECKER_TRANSLATIONUNITVISITOR_H + +#include "MPICheckerAST.h" + +namespace clang { +namespace mpi { + +class TranslationUnitVisitor + : public clang::RecursiveASTVisitor { +public: + TranslationUnitVisitor(clang::ento::BugReporter &BR, + const clang::ento::CheckerBase &CB, + clang::ento::AnalysisManager &AM) + : CheckerAST{BR, CB, AM}, FuncClassifier{AM} {} + + // Visited for each function declaration. + bool VisitFunctionDecl(clang::FunctionDecl *FuncDecl); + + // Visited for each call expression. + bool VisitCallExpr(clang::CallExpr *CE); + + MPICheckerAST CheckerAST; + MPIFunctionClassifier FuncClassifier; +}; + +} // end of namespace: mpi +} // end of namespace: clang + +#endif Index: tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/TranslationUnitVisitor.cpp =================================================================== --- tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/TranslationUnitVisitor.cpp +++ tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/TranslationUnitVisitor.cpp @@ -0,0 +1,41 @@ +//===-- 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 +/// This file defines a translation unit visitor class that invokes +/// AST-based checks on an MPICheckerAST instance during traversal. +/// +//===----------------------------------------------------------------------===// + +#include "TranslationUnitVisitor.h" +#include "MPICheckerPathSensitive.h" + +namespace clang { +namespace mpi { + +bool TranslationUnitVisitor::VisitFunctionDecl(FunctionDecl *FuncDecl) { + // to keep track which function implementation is currently analysed + if (FuncDecl->clang::Decl::hasBody() && !FuncDecl->isInlined()) { + // to make display of function in diagnostics available + CheckerAST.setCurrentlyVisitedFunction(FuncDecl); + } + return true; +} + +bool TranslationUnitVisitor::VisitCallExpr(clang::CallExpr *CE) { + if (FuncClassifier.isMPIType(util::getIdentInfo(CE))) { + CheckerAST.checkBufferTypeMatch(CE); + CheckerAST.checkForInvalidArgs(CE); + } + + return true; +} + +} // end of namespace: mpi +} // end of namespace: clang Index: tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/TypeVisitor.h =================================================================== --- tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/TypeVisitor.h +++ tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/TypeVisitor.h @@ -0,0 +1,99 @@ +//===-- 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 +/// This file defines a type visitor class which collects information about the +// QualType instance passed to its ctor. It detects if the QualType is a +/// typedef, complex type, builtin type and the pointer count. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MPICHECKER_TYPEVISITOR_H +#define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MPICHECKER_TYPEVISITOR_H + +#include "clang/AST/RecursiveASTVisitor.h" + +namespace clang { +namespace mpi { + +class TypeVisitor : public clang::RecursiveASTVisitor { +public: + TypeVisitor(clang::QualType TypeToInspect) : InspectedType{TypeToInspect} { + TraverseType(TypeToInspect); + + // Dereference pointers and remove qualifiers. + DerefUnqualType = TypeToInspect; + while (DerefUnqualType->isPointerType()) { + DerefUnqualType = DerefUnqualType->getPointeeType(); + } + DerefUnqualType = DerefUnqualType.getUnqualifiedType(); + } + + bool VisitTypedefType(clang::TypedefType *TDT) { + TypedefTypeName = TDT->getDecl()->getQualifiedNameAsString(); + IsTypedefType = true; + return true; + } + + bool VisitBuiltinType(clang::BuiltinType *BT) { + BuiltinType = BT; + return true; + } + + bool VisitComplexType(clang::ComplexType *) { + IsComplexCType = true; + return true; + } + + bool VisitTemplateSpecializationType(clang::TemplateSpecializationType *TST) { + TemplateName = TST->getAsCXXRecordDecl()->getNameAsString(); + TemplateSpecializationType = TST; + return true; + } + + bool VisitPointerType(clang::PointerType *) { + ++PointerCount; + return true; + } + + bool VisitArrayType(clang::ArrayType *) { + ++PointerCount; + return true; + } + + // passed qual type + const clang::QualType InspectedType; + + bool isTypedefType() const { return IsTypedefType; } + bool isComplexCType() const { return IsComplexCType; } + const std::string typedefTypeName() const & { return TypedefTypeName; } + const std::string templateName() const & { return TemplateName; } + size_t pointerCount() const { return PointerCount; } + const clang::BuiltinType *builtinType() const { return BuiltinType; } + const clang::TemplateSpecializationType *templateType() const { + return TemplateSpecializationType; + } + const clang::QualType derefUnqualType() const { return DerefUnqualType; } + +private: + bool IsTypedefType{false}; + bool IsComplexCType{false}; + std::string TypedefTypeName; + std::string TemplateName; + size_t PointerCount{0}; + + clang::QualType DerefUnqualType; + clang::TemplateSpecializationType *TemplateSpecializationType = nullptr; + clang::BuiltinType *BuiltinType = nullptr; +}; + +} // end of namespace: mpi +} // end of namespace: clang + +#endif Index: tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/Utility.h =================================================================== --- tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/Utility.h +++ tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/Utility.h @@ -0,0 +1,61 @@ +//===-- Utility.h - 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 +/// This file defines utility helper functions for MPI-Checker. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MPICHECKER_UTILITY_H +#define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MPICHECKER_UTILITY_H + +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" + +namespace util { + +/// Retrieve source range from memory region. The range retrieval +/// is based on the decl obtained from the memory region. +/// For a VarRegion the range of the base region is returned. +/// For a FieldRegion the range of the field is returned. +/// If no declaration is found, an empty source range is returned. +/// The client is responsible for checking if the returned range is valid. +/// +/// \param MR memory region to retrieve the source range for +/// +/// \returns source range for declaration retrieved from memory region +clang::SourceRange sourceRange(const clang::ento::MemRegion *MR); + +/// Returns the source range \p Range as a StringRef. +clang::StringRef sourceRangeAsStringRef(const clang::SourceRange &Range, + clang::ento::AnalysisManager &AM); + +/// Returns an array of strings for \p String split by \p Delimiter. +std::vector split(const std::string &String, char Delimiter); + +/// Retrieve identifier info for a call expression. +/// Returns nullptr if there's no direct callee. +/// +/// \param CE call expression to retrieve ident info for +/// +/// \returns identifier info for passed call expression +const clang::IdentifierInfo *getIdentInfo(const clang::CallExpr *CE); + +/// Get variable name for memory region. The name is obtained from +/// the variable/field declaration retrieved from the memory region. +/// Regions that point to an element of an array are returned as: "arr[0]". +/// Regions that point to a struct are returned as: "st.var". +/// +/// \param MR memory region to get the variable name for +/// +/// \returns variable name for memory region +std::string variableName(const clang::ento::MemRegion *MR); + +} // end of namespace: util + +#endif Index: tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/Utility.cpp =================================================================== --- tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/Utility.cpp +++ tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/Utility.cpp @@ -0,0 +1,103 @@ +//===-- 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 +/// This file defines utility helper functions for MPI-Checker. +/// +//===----------------------------------------------------------------------===// + +#include "clang/Lex/Lexer.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "Utility.h" +#include +#include + +namespace util { + +clang::SourceRange sourceRange(const clang::ento::MemRegion *MR) { + const clang::ento::VarRegion *VR = + clang::dyn_cast(MR->getBaseRegion()); + + const clang::ento::FieldRegion *FR = + clang::dyn_cast(MR); + + if (FR) { + return FR->getDecl()->getSourceRange(); + } else if (VR) { + return VR->getDecl()->getSourceRange(); + } else { + // non valid source range (can be checked by client) + return clang::SourceRange{}; + } +} + +clang::StringRef sourceRangeAsStringRef(const clang::SourceRange &Range, + clang::ento::AnalysisManager &AM) { + auto CharRange = clang::CharSourceRange::getTokenRange(Range); + return clang::Lexer::getSourceText(CharRange, AM.getSourceManager(), + clang::LangOptions()); +} + +std::vector split(const std::string &String, char Delimiter) { + std::vector Elements; + std::stringstream StringStream(String); + std::string Item; + while (std::getline(StringStream, Item, Delimiter)) { + Elements.push_back(Item); + } + return Elements; +} + +std::string variableName(const clang::ento::MemRegion *MR) { + const clang::ento::VarRegion *VR = + clang::dyn_cast(MR->getBaseRegion()); + + const clang::ento::FieldRegion *FR = + clang::dyn_cast(MR); + + const clang::ento::ElementRegion *ER = + MR->getAs(); + + std::string VariableName{""}; + + // members, fields + if (FR) { + VariableName = VR->getDecl()->getNameAsString() + "." + + FR->getDecl()->getNameAsString(); + } + // variable + else if (VR) { + VariableName = VR->getDecl()->getNameAsString(); + } else { + // Get var-decl-name for symbolic region. + } + + if (ER) { + llvm::APSInt IndexInArray; + IndexInArray = + ER->getIndex().getAs()->getValue(); + + llvm::SmallVector intValAsString; + IndexInArray.toString(intValAsString); + std::string idx{intValAsString.begin(), intValAsString.end()}; + return VariableName + "[" + idx + "]"; + } else { + return VariableName; + } +} + +const clang::IdentifierInfo *getIdentInfo(const clang::CallExpr *CE) { + if (CE->getDirectCallee()) { + return CE->getDirectCallee()->getIdentifier(); + } else { + return nullptr; + } +} + +} // end of namespace: util Index: tools/clang/test/Analysis/MPIChecker.cpp =================================================================== --- tools/clang/test/Analysis/MPIChecker.cpp +++ tools/clang/test/Analysis/MPIChecker.cpp @@ -0,0 +1,527 @@ +// RUN: %clang_cc1 -analyze -analyzer-checker=mpi.MPI-Checker -verify %s + +// MPI-Checker makes no assumptions about details of an MPI implementation. +// Typedefs and constants are compared as strings. + +#define NULL 0 + +// mock types +typedef int MPI_Datatype; +typedef int MPI_Comm; +typedef int MPI_Request; +typedef int MPI_Status; +typedef int MPI_Op; +typedef int int8_t; +typedef int uint8_t; +typedef int uint16_t; +typedef int int64_t; +namespace std { template struct complex { T real; T imag; }; } + +// mock constants +#define MPI_DATATYPE_NULL 0 +#define MPI_CHAR 0 +#define MPI_BYTE 0 +#define MPI_INT 0 +#define MPI_LONG 0 +#define MPI_LONG_DOUBLE 0 +#define MPI_UNSIGNED 0 +#define MPI_INT8_T 0 +#define MPI_UINT8_T 0 +#define MPI_UINT16_T 0 +#define MPI_C_LONG_DOUBLE_COMPLEX 0 +#define MPI_FLOAT 0 +#define MPI_DOUBLE 0 +#define MPI_CXX_BOOL 0 +#define MPI_CXX_FLOAT_COMPLEX 0 +#define MPI_CXX_DOUBLE_COMPLEX 0 +#define MPI_CXX_LONG_DOUBLE_COMPLEX 0 +#define MPI_IN_PLACE 0 +#define MPI_COMM_WORLD 0 +#define MPI_STATUS_IGNORE 0 +#define MPI_STATUSES_IGNORE 0 +#define MPI_SUM 0 + +int MPI_Comm_size(MPI_Comm, int *); +int MPI_Comm_rank(MPI_Comm, int *); +int MPI_Send(const void *, int, MPI_Datatype, int, int, MPI_Comm); +int MPI_Recv(void *, int, MPI_Datatype, int, int, MPI_Comm, MPI_Status *); +int MPI_Isend(const void *, int, MPI_Datatype, int, int, MPI_Comm, + MPI_Request *); +int MPI_Irecv(void *, int, MPI_Datatype, int, int, MPI_Comm, MPI_Request *); +int MPI_Wait(MPI_Request *, MPI_Status *); +int MPI_Waitall(int, MPI_Request[], MPI_Status[]); +int MPI_Reduce(const void *, void *, int, MPI_Datatype, MPI_Op, int, MPI_Comm); +int MPI_Ireduce(const void *, void *, int, MPI_Datatype, MPI_Op, int, MPI_Comm, + MPI_Request *); +int MPI_Bcast(void *, int count, MPI_Datatype, int, MPI_Comm); + +//integration tests------------------------------------------------------- +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, 0, MPI_COMM_WORLD, &req[0]); + MPI_Irecv(&buf, 1, MPI_DOUBLE, rank - 1, 0, 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 70.}} + } +} + +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, 1, MPI_COMM_WORLD, &sendReq1); + MPI_Irecv(&buf, 1, MPI_DOUBLE, rank - 1, 1, 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 85.}} + } +} + +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 100.}} +} + +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, 2, MPI_COMM_WORLD, &sendReq1); + MPI_Irecv(&buf, 1, MPI_DOUBLE, rank - 1, 2, MPI_COMM_WORLD, &recvReq1); + MPI_Wait(&recvReq1, MPI_STATUS_IGNORE); + } +} // expected-warning{{'MPI_Isend' in line 112, 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, 3, MPI_COMM_WORLD, &sendReq1); + MPI_Irecv(&buf, 1, MPI_DOUBLE, rank - 1, 3, 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, 4, MPI_COMM_WORLD, &sendReq1); + MPI_Irecv(&buf, 1, MPI_DOUBLE, rank - 1, 4, 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, 5, MPI_COMM_WORLD, &sendReq1); + MPI_Irecv(&buf, 1, MPI_DOUBLE, rank - 1, 5, 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, 6, MPI_COMM_WORLD, &sendReq1); + MPI_Irecv(&buf, 1, MPI_DOUBLE, rank - 1, 6, MPI_COMM_WORLD, &sendReq1); // expected-warning{{Request 'sendReq1' is already in use by nonblocking call 'MPI_Isend' in line 172.}} + 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, 0, MPI_COMM_WORLD, &req); + MPI_Ireduce(MPI_IN_PLACE, &buf, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD, &req); // expected-warning{{Request 'req' is already in use by nonblocking call 'MPI_Ireduce' in line 184.}} + 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 197.}} + MPI_Wait(&rs.req, MPI_STATUS_IGNORE); +} + +void missingNonBlocking() { + int rank = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + 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); +} + +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, 7, 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, 7, 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, 7, MPI_COMM_WORLD); + } else { + MPI_Recv(bufP, 1, MPI_DOUBLE, rank - 1, 7, 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, 8, 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, 8, 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, 8, MPI_COMM_WORLD); + } else { + MPI_Recv(&buf, 1, MPI_LONG_DOUBLE, rank - 1, 8, 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, 9, 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, 9, 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, 10, 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, 10, 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, 10, MPI_COMM_WORLD); + } else { + MPI_Recv(&buf, 1, MPI_UINT8_T, rank - 1, 10, 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 typeMatching21() { + bool *buf = NULL; + MPI_Reduce(MPI_IN_PLACE, buf, 1, MPI_FLOAT, MPI_SUM, 0, MPI_COMM_WORLD); // expected-warning{{Buffer type '_Bool' and specified MPI type 'MPI_FLOAT' do not match.}} + MPI_Reduce(MPI_IN_PLACE, buf, 1, MPI_CXX_BOOL, MPI_SUM, 0, MPI_COMM_WORLD); +} + +void typeMatching22() { + MPI_Reduce(MPI_IN_PLACE, NULL, 0, MPI_DATATYPE_NULL, MPI_SUM, 0, MPI_COMM_WORLD); + MPI_Reduce(MPI_IN_PLACE, NULL, 0, MPI_LONG, MPI_SUM, 0, MPI_COMM_WORLD); // expected-warning{{Buffer type 'null pointer' and specified MPI type 'MPI_LONG' do not match.}} +} + +void typeMatching23() { + std::complex buf; + MPI_Reduce(MPI_IN_PLACE, &buf, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); // expected-warning{{Buffer type 'std::complex' and specified MPI type 'MPI_DOUBLE' do not match.}} + MPI_Reduce(MPI_IN_PLACE, &buf, 1, MPI_CXX_FLOAT_COMPLEX, MPI_SUM, 0, MPI_COMM_WORLD); // expected-warning{{Buffer type 'std::complex' and specified MPI type 'MPI_CXX_FLOAT_COMPLEX' do not match.}} + MPI_Reduce(MPI_IN_PLACE, &buf, 1, MPI_CXX_DOUBLE_COMPLEX, MPI_SUM, 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, 11, 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, 11, 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, 12, 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, 12, 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, 13, 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, 13, 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(), 14, 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(), 14, 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 +++ tools/clang/unittests/StaticAnalyzer/CMakeLists.txt @@ -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 +++ tools/clang/unittests/StaticAnalyzer/MPI-Checker/CMakeLists.txt @@ -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 +++ tools/clang/unittests/StaticAnalyzer/MPI-Checker/ContainerTest.cpp @@ -0,0 +1,10 @@ +#include "gtest/gtest.h" +#include +#include "Container.h" + +TEST(Container, contains) { + std::vector v{0, 1, 2, 3, 4, 5, 6, 8, 9}; + EXPECT_TRUE(cont::contains(v, 0)); + EXPECT_TRUE(cont::contains(v, 3)); + EXPECT_TRUE(cont::contains(v, 9)); +} Index: tools/clang/unittests/StaticAnalyzer/MPI-Checker/UtilityTest.cpp =================================================================== --- tools/clang/unittests/StaticAnalyzer/MPI-Checker/UtilityTest.cpp +++ tools/clang/unittests/StaticAnalyzer/MPI-Checker/UtilityTest.cpp @@ -0,0 +1,12 @@ +#include "gtest/gtest.h" +#include "Utility.h" + +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"); +}