diff options
Diffstat (limited to 'clang/lib/StaticAnalyzer/Checkers/MPI-Checker')
6 files changed, 870 insertions, 0 deletions
diff --git a/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.cpp b/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.cpp new file mode 100644 index 000000000000..bbf2ddec5762 --- /dev/null +++ b/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.cpp @@ -0,0 +1,117 @@ +//===-- MPIBugReporter.cpp - bug reporter -----------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file defines prefabricated reports which are emitted in +/// case of MPI related bugs, detected by path-sensitive analysis. +/// +//===----------------------------------------------------------------------===// + +#include "MPIBugReporter.h" +#include "MPIChecker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" + +namespace clang { +namespace ento { +namespace mpi { + +void MPIBugReporter::reportDoubleNonblocking( +    const CallEvent &MPICallEvent, const ento::mpi::Request &Req, +    const MemRegion *const RequestRegion, +    const ExplodedNode *const ExplNode, +    BugReporter &BReporter) const { + +  std::string ErrorText; +  ErrorText = "Double nonblocking on request " + +              RequestRegion->getDescriptiveName() + ". "; + +  auto Report = std::make_unique<PathSensitiveBugReport>( +      *DoubleNonblockingBugType, ErrorText, ExplNode); + +  Report->addRange(MPICallEvent.getSourceRange()); +  SourceRange Range = RequestRegion->sourceRange(); + +  if (Range.isValid()) +    Report->addRange(Range); + +  Report->addVisitor(std::make_unique<RequestNodeVisitor>( +      RequestRegion, "Request is previously used by nonblocking call here. ")); +  Report->markInteresting(RequestRegion); + +  BReporter.emitReport(std::move(Report)); +} + +void MPIBugReporter::reportMissingWait( +    const ento::mpi::Request &Req, const MemRegion *const RequestRegion, +    const ExplodedNode *const ExplNode, +    BugReporter &BReporter) const { +  std::string ErrorText{"Request " + RequestRegion->getDescriptiveName() + +                        " has no matching wait. "}; + +  auto Report = std::make_unique<PathSensitiveBugReport>(*MissingWaitBugType, +                                                         ErrorText, ExplNode); + +  SourceRange Range = RequestRegion->sourceRange(); +  if (Range.isValid()) +    Report->addRange(Range); +  Report->addVisitor(std::make_unique<RequestNodeVisitor>( +      RequestRegion, "Request is previously used by nonblocking call here. ")); +  Report->markInteresting(RequestRegion); + +  BReporter.emitReport(std::move(Report)); +} + +void MPIBugReporter::reportUnmatchedWait( +    const CallEvent &CE, const clang::ento::MemRegion *const RequestRegion, +    const ExplodedNode *const ExplNode, +    BugReporter &BReporter) const { +  std::string ErrorText{"Request " + RequestRegion->getDescriptiveName() + +                        " has no matching nonblocking call. "}; + +  auto Report = std::make_unique<PathSensitiveBugReport>(*UnmatchedWaitBugType, +                                                         ErrorText, ExplNode); + +  Report->addRange(CE.getSourceRange()); +  SourceRange Range = RequestRegion->sourceRange(); +  if (Range.isValid()) +    Report->addRange(Range); + +  BReporter.emitReport(std::move(Report)); +} + +PathDiagnosticPieceRef +MPIBugReporter::RequestNodeVisitor::VisitNode(const ExplodedNode *N, +                                              BugReporterContext &BRC, +                                              PathSensitiveBugReport &BR) { + +  if (IsNodeFound) +    return nullptr; + +  const Request *const Req = N->getState()->get<RequestMap>(RequestRegion); +  assert(Req && "The region must be tracked and alive, given that we've " +                "just emitted a report against it"); +  const Request *const PrevReq = +      N->getFirstPred()->getState()->get<RequestMap>(RequestRegion); + +  // Check if request was previously unused or in a different state. +  if (!PrevReq || (Req->CurrentState != PrevReq->CurrentState)) { +    IsNodeFound = true; + +    ProgramPoint P = N->getFirstPred()->getLocation(); +    PathDiagnosticLocation L = +        PathDiagnosticLocation::create(P, BRC.getSourceManager()); + +    return std::make_shared<PathDiagnosticEventPiece>(L, ErrorText); +  } + +  return nullptr; +} + +} // end of namespace: mpi +} // end of namespace: ento +} // end of namespace: clang diff --git a/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.h b/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.h new file mode 100644 index 000000000000..9871da026b04 --- /dev/null +++ b/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.h @@ -0,0 +1,107 @@ +//===-- MPIBugReporter.h - bug reporter -----------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file defines prefabricated reports which are emitted in +/// case of MPI related bugs, detected by path-sensitive analysis. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MPICHECKER_MPIBUGREPORTER_H +#define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MPICHECKER_MPIBUGREPORTER_H + +#include "MPITypes.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" + +namespace clang { +namespace ento { +namespace mpi { + +class MPIBugReporter { +public: +  MPIBugReporter(const CheckerBase &CB) { +    UnmatchedWaitBugType.reset(new BugType(&CB, "Unmatched wait", MPIError)); +    DoubleNonblockingBugType.reset( +        new BugType(&CB, "Double nonblocking", MPIError)); +    MissingWaitBugType.reset(new BugType(&CB, "Missing wait", MPIError)); +  } + +  /// Report duplicate request use by nonblocking calls without intermediate +  /// wait. +  /// +  /// \param MPICallEvent MPI call that caused the double nonblocking +  /// \param Req request that was used by two nonblocking calls in sequence +  /// \param RequestRegion memory region of the request +  /// \param ExplNode node in the graph the bug appeared at +  /// \param BReporter bug reporter for current context +  void reportDoubleNonblocking(const CallEvent &MPICallEvent, +                               const Request &Req, +                               const MemRegion *const RequestRegion, +                               const ExplodedNode *const ExplNode, +                              BugReporter &BReporter) const; + +  /// Report a missing wait for a nonblocking call. +  /// +  /// \param Req request that is not matched by a wait +  /// \param RequestRegion memory region of the request +  /// \param ExplNode node in the graph the bug appeared at +  /// \param BReporter bug reporter for current context +  void reportMissingWait(const Request &Req, +                         const MemRegion *const RequestRegion, +                         const ExplodedNode *const ExplNode, +                         BugReporter &BReporter) const; + +  /// Report a wait on a request that has not been used at all before. +  /// +  /// \param CE wait call that uses the request +  /// \param RequestRegion memory region of the request +  /// \param ExplNode node in the graph the bug appeared at +  /// \param BReporter bug reporter for current context +  void reportUnmatchedWait(const CallEvent &CE, +                           const MemRegion *const RequestRegion, +                           const ExplodedNode *const ExplNode, +                           BugReporter &BReporter) const; + +private: +  const std::string MPIError = "MPI Error"; + +  // path-sensitive bug types +  std::unique_ptr<BugType> UnmatchedWaitBugType; +  std::unique_ptr<BugType> MissingWaitBugType; +  std::unique_ptr<BugType> DoubleNonblockingBugType; + +  /// Bug visitor class to find the node where the request region was previously +  /// used in order to include it into the BugReport path. +  class RequestNodeVisitor : public BugReporterVisitor { +  public: +    RequestNodeVisitor(const MemRegion *const MemoryRegion, +                       const std::string &ErrText) +        : RequestRegion(MemoryRegion), ErrorText(ErrText) {} + +    void Profile(llvm::FoldingSetNodeID &ID) const override { +      static int X = 0; +      ID.AddPointer(&X); +      ID.AddPointer(RequestRegion); +    } + +    PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, +                                     BugReporterContext &BRC, +                                     PathSensitiveBugReport &BR) override; + +  private: +    const MemRegion *const RequestRegion; +    bool IsNodeFound = false; +    std::string ErrorText; +  }; +}; + +} // end of namespace: mpi +} // end of namespace: ento +} // end of namespace: clang + +#endif diff --git a/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.cpp new file mode 100644 index 000000000000..7f9ba0de1dc2 --- /dev/null +++ b/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.cpp @@ -0,0 +1,193 @@ +//===-- MPIChecker.cpp - Checker Entry Point Class --------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \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. +/// The checker defines path-sensitive checks, to verify correct usage of the +/// MPI API. +/// +//===----------------------------------------------------------------------===// + +#include "MPIChecker.h" +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" + +namespace clang { +namespace ento { +namespace mpi { + +void MPIChecker::checkDoubleNonblocking(const CallEvent &PreCallEvent, +                                        CheckerContext &Ctx) const { +  if (!FuncClassifier->isNonBlockingType(PreCallEvent.getCalleeIdentifier())) { +    return; +  } +  const MemRegion *const MR = +      PreCallEvent.getArgSVal(PreCallEvent.getNumArgs() - 1).getAsRegion(); +  if (!MR) +    return; +  const ElementRegion *const ER = dyn_cast<ElementRegion>(MR); + +  // The region must be typed, in order to reason about it. +  if (!isa<TypedRegion>(MR) || (ER && !isa<TypedRegion>(ER->getSuperRegion()))) +    return; + +  ProgramStateRef State = Ctx.getState(); +  const Request *const Req = State->get<RequestMap>(MR); + +  // double nonblocking detected +  if (Req && Req->CurrentState == Request::State::Nonblocking) { +    ExplodedNode *ErrorNode = Ctx.generateNonFatalErrorNode(); +    BReporter.reportDoubleNonblocking(PreCallEvent, *Req, MR, ErrorNode, +                                      Ctx.getBugReporter()); +    Ctx.addTransition(ErrorNode->getState(), ErrorNode); +  } +  // no error +  else { +    State = State->set<RequestMap>(MR, Request::State::Nonblocking); +    Ctx.addTransition(State); +  } +} + +void MPIChecker::checkUnmatchedWaits(const CallEvent &PreCallEvent, +                                     CheckerContext &Ctx) const { +  if (!FuncClassifier->isWaitType(PreCallEvent.getCalleeIdentifier())) +    return; +  const MemRegion *const MR = topRegionUsedByWait(PreCallEvent); +  if (!MR) +    return; +  const ElementRegion *const ER = dyn_cast<ElementRegion>(MR); + +  // The region must be typed, in order to reason about it. +  if (!isa<TypedRegion>(MR) || (ER && !isa<TypedRegion>(ER->getSuperRegion()))) +    return; + +  llvm::SmallVector<const MemRegion *, 2> ReqRegions; +  allRegionsUsedByWait(ReqRegions, MR, PreCallEvent, Ctx); +  if (ReqRegions.empty()) +    return; + +  ProgramStateRef State = Ctx.getState(); +  static CheckerProgramPointTag Tag("MPI-Checker", "UnmatchedWait"); +  ExplodedNode *ErrorNode{nullptr}; + +  // Check all request regions used by the wait function. +  for (const auto &ReqRegion : ReqRegions) { +    const Request *const Req = State->get<RequestMap>(ReqRegion); +    State = State->set<RequestMap>(ReqRegion, Request::State::Wait); +    if (!Req) { +      if (!ErrorNode) { +        ErrorNode = Ctx.generateNonFatalErrorNode(State, &Tag); +        State = ErrorNode->getState(); +      } +      // A wait has no matching nonblocking call. +      BReporter.reportUnmatchedWait(PreCallEvent, ReqRegion, ErrorNode, +                                    Ctx.getBugReporter()); +    } +  } + +  if (!ErrorNode) { +    Ctx.addTransition(State); +  } else { +    Ctx.addTransition(State, ErrorNode); +  } +} + +void MPIChecker::checkMissingWaits(SymbolReaper &SymReaper, +                                   CheckerContext &Ctx) const { +  ProgramStateRef State = Ctx.getState(); +  const auto &Requests = State->get<RequestMap>(); +  if (Requests.isEmpty()) +    return; + +  static CheckerProgramPointTag Tag("MPI-Checker", "MissingWait"); +  ExplodedNode *ErrorNode{nullptr}; + +  auto ReqMap = State->get<RequestMap>(); +  for (const auto &Req : ReqMap) { +    if (!SymReaper.isLiveRegion(Req.first)) { +      if (Req.second.CurrentState == Request::State::Nonblocking) { + +        if (!ErrorNode) { +          ErrorNode = Ctx.generateNonFatalErrorNode(State, &Tag); +          State = ErrorNode->getState(); +        } +        BReporter.reportMissingWait(Req.second, Req.first, ErrorNode, +                                    Ctx.getBugReporter()); +      } +      State = State->remove<RequestMap>(Req.first); +    } +  } + +  // Transition to update the state regarding removed requests. +  if (!ErrorNode) { +    Ctx.addTransition(State); +  } else { +    Ctx.addTransition(State, ErrorNode); +  } +} + +const MemRegion *MPIChecker::topRegionUsedByWait(const 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 MPIChecker::allRegionsUsedByWait( +    llvm::SmallVector<const MemRegion *, 2> &ReqRegions, +    const MemRegion *const MR, const CallEvent &CE, CheckerContext &Ctx) const { + +  MemRegionManager *const RegionManager = MR->getMemRegionManager(); + +  if (FuncClassifier->isMPI_Waitall(CE.getCalleeIdentifier())) { +    const SubRegion *SuperRegion{nullptr}; +    if (const ElementRegion *const ER = MR->getAs<ElementRegion>()) { +      SuperRegion = cast<SubRegion>(ER->getSuperRegion()); +    } + +    // A single request is passed to MPI_Waitall. +    if (!SuperRegion) { +      ReqRegions.push_back(MR); +      return; +    } + +    const auto &Size = Ctx.getStoreManager().getSizeInElements( +        Ctx.getState(), SuperRegion, +        CE.getArgExpr(1)->getType()->getPointeeType()); +    const llvm::APSInt &ArrSize = Size.getAs<nonloc::ConcreteInt>()->getValue(); + +    for (size_t i = 0; i < ArrSize; ++i) { +      const NonLoc Idx = Ctx.getSValBuilder().makeArrayIndex(i); + +      const ElementRegion *const ER = RegionManager->getElementRegion( +          CE.getArgExpr(1)->getType()->getPointeeType(), Idx, SuperRegion, +          Ctx.getASTContext()); + +      ReqRegions.push_back(ER->getAs<MemRegion>()); +    } +  } else if (FuncClassifier->isMPI_Wait(CE.getCalleeIdentifier())) { +    ReqRegions.push_back(MR); +  } +} + +} // end of namespace: mpi +} // end of namespace: ento +} // end of namespace: clang + +// Registers the checker for static analysis. +void clang::ento::registerMPIChecker(CheckerManager &MGR) { +  MGR.registerChecker<clang::ento::mpi::MPIChecker>(); +} + +bool clang::ento::shouldRegisterMPIChecker(const LangOptions &LO) { +  return true; +} diff --git a/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.h b/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.h new file mode 100644 index 000000000000..ce9f1afac209 --- /dev/null +++ b/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.h @@ -0,0 +1,104 @@ +//===-- MPIChecker.h - Verify MPI API usage- --------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \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. +/// The checker defines path-sensitive checks, to verify correct usage of the +/// MPI API. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MPICHECKER_MPICHECKER_H +#define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MPICHECKER_MPICHECKER_H + +#include "MPIBugReporter.h" +#include "MPITypes.h" +#include "clang/StaticAnalyzer/Checkers/MPIFunctionClassifier.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" + +namespace clang { +namespace ento { +namespace mpi { + +class MPIChecker : public Checker<check::PreCall, check::DeadSymbols> { +public: +  MPIChecker() : BReporter(*this) {} + +  // path-sensitive callbacks +  void checkPreCall(const CallEvent &CE, CheckerContext &Ctx) const { +    dynamicInit(Ctx); +    checkUnmatchedWaits(CE, Ctx); +    checkDoubleNonblocking(CE, Ctx); +  } + +  void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &Ctx) const { +    dynamicInit(Ctx); +    checkMissingWaits(SymReaper, Ctx); +  } + +  void dynamicInit(CheckerContext &Ctx) const { +    if (FuncClassifier) +      return; +    const_cast<std::unique_ptr<MPIFunctionClassifier> &>(FuncClassifier) +        .reset(new MPIFunctionClassifier{Ctx.getASTContext()}); +  } + +  /// 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 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 checkUnmatchedWaits(const clang::ento::CallEvent &PreCallEvent, +                           clang::ento::CheckerContext &Ctx) const; + +  /// Check if a nonblocking call is not matched by a wait. +  /// If a memory region is not alive and the last function using the +  /// request was a nonblocking call, this is rated as a missing wait. +  void checkMissingWaits(clang::ento::SymbolReaper &SymReaper, +                         clang::ento::CheckerContext &Ctx) const; + +private: +  /// 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 allRegionsUsedByWait( +      llvm::SmallVector<const clang::ento::MemRegion *, 2> &ReqRegions, +      const clang::ento::MemRegion *const MR, const clang::ento::CallEvent &CE, +      clang::ento::CheckerContext &Ctx) const; + +  /// 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 * +  topRegionUsedByWait(const clang::ento::CallEvent &CE) const; + +  const std::unique_ptr<MPIFunctionClassifier> FuncClassifier; +  MPIBugReporter BReporter; +}; + +} // end of namespace: mpi +} // end of namespace: ento +} // end of namespace: clang + +#endif diff --git a/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIFunctionClassifier.cpp b/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIFunctionClassifier.cpp new file mode 100644 index 000000000000..277b3ed2e105 --- /dev/null +++ b/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIFunctionClassifier.cpp @@ -0,0 +1,283 @@ +//===-- MPIFunctionClassifier.cpp - classifies MPI functions ----*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file defines functionality to identify and classify MPI functions. +/// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/MPIFunctionClassifier.h" +#include "llvm/ADT/STLExtras.h" + +namespace clang { +namespace ento { +namespace mpi { + +void MPIFunctionClassifier::identifierInit(ASTContext &ASTCtx) { +  // Initialize function identifiers. +  initPointToPointIdentifiers(ASTCtx); +  initCollectiveIdentifiers(ASTCtx); +  initAdditionalIdentifiers(ASTCtx); +} + +void MPIFunctionClassifier::initPointToPointIdentifiers(ASTContext &ASTCtx) { +  // Copy identifiers into the correct classification containers. +  IdentInfo_MPI_Send = &ASTCtx.Idents.get("MPI_Send"); +  MPIPointToPointTypes.push_back(IdentInfo_MPI_Send); +  MPIType.push_back(IdentInfo_MPI_Send); +  assert(IdentInfo_MPI_Send); + +  IdentInfo_MPI_Isend = &ASTCtx.Idents.get("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"); +  MPIPointToPointTypes.push_back(IdentInfo_MPI_Ssend); +  MPIType.push_back(IdentInfo_MPI_Ssend); +  assert(IdentInfo_MPI_Ssend); + +  IdentInfo_MPI_Issend = &ASTCtx.Idents.get("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"); +  MPIPointToPointTypes.push_back(IdentInfo_MPI_Bsend); +  MPIType.push_back(IdentInfo_MPI_Bsend); +  assert(IdentInfo_MPI_Bsend); + +  IdentInfo_MPI_Ibsend = &ASTCtx.Idents.get("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"); +  MPIPointToPointTypes.push_back(IdentInfo_MPI_Rsend); +  MPIType.push_back(IdentInfo_MPI_Rsend); +  assert(IdentInfo_MPI_Rsend); + +  IdentInfo_MPI_Irsend = &ASTCtx.Idents.get("MPI_Irsend"); +  MPIPointToPointTypes.push_back(IdentInfo_MPI_Irsend); +  MPIType.push_back(IdentInfo_MPI_Irsend); +  assert(IdentInfo_MPI_Irsend); + +  IdentInfo_MPI_Recv = &ASTCtx.Idents.get("MPI_Recv"); +  MPIPointToPointTypes.push_back(IdentInfo_MPI_Recv); +  MPIType.push_back(IdentInfo_MPI_Recv); +  assert(IdentInfo_MPI_Recv); + +  IdentInfo_MPI_Irecv = &ASTCtx.Idents.get("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(ASTContext &ASTCtx) { +  // 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); +  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); +  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); +  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); +  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); +  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); +  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); +  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(ASTContext &ASTCtx) { +  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_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 llvm::is_contained(MPIType, IdentInfo); +} + +bool MPIFunctionClassifier::isNonBlockingType( +    const IdentifierInfo *IdentInfo) const { +  return llvm::is_contained(MPINonBlockingTypes, IdentInfo); +} + +// point-to-point identifiers +bool MPIFunctionClassifier::isPointToPointType( +    const IdentifierInfo *IdentInfo) const { +  return llvm::is_contained(MPIPointToPointTypes, IdentInfo); +} + +// collective identifiers +bool MPIFunctionClassifier::isCollectiveType( +    const IdentifierInfo *IdentInfo) const { +  return llvm::is_contained(MPICollectiveTypes, IdentInfo); +} + +bool MPIFunctionClassifier::isCollToColl( +    const IdentifierInfo *IdentInfo) const { +  return llvm::is_contained(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_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::isWaitType(const IdentifierInfo *IdentInfo) const { +  return IdentInfo == IdentInfo_MPI_Wait || IdentInfo == IdentInfo_MPI_Waitall; +} + +} // end of namespace: mpi +} // end of namespace: ento +} // end of namespace: clang diff --git a/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPITypes.h b/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPITypes.h new file mode 100644 index 000000000000..fe0fb2a4d0e7 --- /dev/null +++ b/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPITypes.h @@ -0,0 +1,66 @@ +//===-- MPITypes.h - Functionality to model MPI concepts --------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file provides definitions to model concepts of MPI. 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/Checkers/MPIFunctionClassifier.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "llvm/ADT/SmallSet.h" + +namespace clang { +namespace ento { +namespace mpi { + +class Request { +public: +  enum State : unsigned char { Nonblocking, Wait }; + +  Request(State S) : CurrentState{S} {} + +  void Profile(llvm::FoldingSetNodeID &Id) const { +    Id.AddInteger(CurrentState); +  } + +  bool operator==(const Request &ToCompare) const { +    return CurrentState == ToCompare.CurrentState; +  } + +  const State CurrentState; +}; + +// The RequestMap stores MPI requests which are identified by their memory +// region. Requests are used in MPI to complete nonblocking operations with wait +// operations. A custom map implementation is used, in order to make it +// available in an arbitrary amount of translation units. +struct RequestMap {}; +typedef llvm::ImmutableMap<const clang::ento::MemRegion *, +                           clang::ento::mpi::Request> +    RequestMapImpl; + +} // end of namespace: mpi + +template <> +struct ProgramStateTrait<mpi::RequestMap> +    : public ProgramStatePartialTrait<mpi::RequestMapImpl> { +  static void *GDMIndex() { +    static int index = 0; +    return &index; +  } +}; + +} // end of namespace: ento +} // end of namespace: clang +#endif  | 
