1303233Sdim//===-- MPIChecker.cpp - Checker Entry Point Class --------------*- C++ -*-===//
2303233Sdim//
3353358Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4353358Sdim// See https://llvm.org/LICENSE.txt for license information.
5353358Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6303233Sdim//
7303233Sdim//===----------------------------------------------------------------------===//
8303233Sdim///
9303233Sdim/// \file
10303233Sdim/// This file defines the main class of MPI-Checker which serves as an entry
11303233Sdim/// point. It is created once for each translation unit analysed.
12303233Sdim/// The checker defines path-sensitive checks, to verify correct usage of the
13303233Sdim/// MPI API.
14303233Sdim///
15303233Sdim//===----------------------------------------------------------------------===//
16303233Sdim
17303233Sdim#include "MPIChecker.h"
18344779Sdim#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
19303233Sdim
20303233Sdimnamespace clang {
21303233Sdimnamespace ento {
22303233Sdimnamespace mpi {
23303233Sdim
24303233Sdimvoid MPIChecker::checkDoubleNonblocking(const CallEvent &PreCallEvent,
25303233Sdim                                        CheckerContext &Ctx) const {
26303233Sdim  if (!FuncClassifier->isNonBlockingType(PreCallEvent.getCalleeIdentifier())) {
27303233Sdim    return;
28303233Sdim  }
29303233Sdim  const MemRegion *const MR =
30303233Sdim      PreCallEvent.getArgSVal(PreCallEvent.getNumArgs() - 1).getAsRegion();
31303233Sdim  if (!MR)
32303233Sdim    return;
33303233Sdim  const ElementRegion *const ER = dyn_cast<ElementRegion>(MR);
34303233Sdim
35303233Sdim  // The region must be typed, in order to reason about it.
36303233Sdim  if (!isa<TypedRegion>(MR) || (ER && !isa<TypedRegion>(ER->getSuperRegion())))
37303233Sdim    return;
38303233Sdim
39303233Sdim  ProgramStateRef State = Ctx.getState();
40303233Sdim  const Request *const Req = State->get<RequestMap>(MR);
41303233Sdim
42303233Sdim  // double nonblocking detected
43303233Sdim  if (Req && Req->CurrentState == Request::State::Nonblocking) {
44303233Sdim    ExplodedNode *ErrorNode = Ctx.generateNonFatalErrorNode();
45314564Sdim    BReporter.reportDoubleNonblocking(PreCallEvent, *Req, MR, ErrorNode,
46314564Sdim                                      Ctx.getBugReporter());
47303233Sdim    Ctx.addTransition(ErrorNode->getState(), ErrorNode);
48303233Sdim  }
49303233Sdim  // no error
50303233Sdim  else {
51303233Sdim    State = State->set<RequestMap>(MR, Request::State::Nonblocking);
52303233Sdim    Ctx.addTransition(State);
53303233Sdim  }
54303233Sdim}
55303233Sdim
56303233Sdimvoid MPIChecker::checkUnmatchedWaits(const CallEvent &PreCallEvent,
57303233Sdim                                     CheckerContext &Ctx) const {
58303233Sdim  if (!FuncClassifier->isWaitType(PreCallEvent.getCalleeIdentifier()))
59303233Sdim    return;
60303233Sdim  const MemRegion *const MR = topRegionUsedByWait(PreCallEvent);
61303233Sdim  if (!MR)
62303233Sdim    return;
63303233Sdim  const ElementRegion *const ER = dyn_cast<ElementRegion>(MR);
64303233Sdim
65303233Sdim  // The region must be typed, in order to reason about it.
66303233Sdim  if (!isa<TypedRegion>(MR) || (ER && !isa<TypedRegion>(ER->getSuperRegion())))
67303233Sdim    return;
68303233Sdim
69303233Sdim  llvm::SmallVector<const MemRegion *, 2> ReqRegions;
70303233Sdim  allRegionsUsedByWait(ReqRegions, MR, PreCallEvent, Ctx);
71303233Sdim  if (ReqRegions.empty())
72303233Sdim    return;
73303233Sdim
74303233Sdim  ProgramStateRef State = Ctx.getState();
75303233Sdim  static CheckerProgramPointTag Tag("MPI-Checker", "UnmatchedWait");
76303233Sdim  ExplodedNode *ErrorNode{nullptr};
77303233Sdim
78303233Sdim  // Check all request regions used by the wait function.
79303233Sdim  for (const auto &ReqRegion : ReqRegions) {
80303233Sdim    const Request *const Req = State->get<RequestMap>(ReqRegion);
81303233Sdim    State = State->set<RequestMap>(ReqRegion, Request::State::Wait);
82303233Sdim    if (!Req) {
83303233Sdim      if (!ErrorNode) {
84303233Sdim        ErrorNode = Ctx.generateNonFatalErrorNode(State, &Tag);
85303233Sdim        State = ErrorNode->getState();
86303233Sdim      }
87303233Sdim      // A wait has no matching nonblocking call.
88314564Sdim      BReporter.reportUnmatchedWait(PreCallEvent, ReqRegion, ErrorNode,
89314564Sdim                                    Ctx.getBugReporter());
90303233Sdim    }
91303233Sdim  }
92303233Sdim
93303233Sdim  if (!ErrorNode) {
94303233Sdim    Ctx.addTransition(State);
95303233Sdim  } else {
96303233Sdim    Ctx.addTransition(State, ErrorNode);
97303233Sdim  }
98303233Sdim}
99303233Sdim
100303233Sdimvoid MPIChecker::checkMissingWaits(SymbolReaper &SymReaper,
101303233Sdim                                   CheckerContext &Ctx) const {
102303233Sdim  ProgramStateRef State = Ctx.getState();
103303233Sdim  const auto &Requests = State->get<RequestMap>();
104303233Sdim  if (Requests.isEmpty())
105303233Sdim    return;
106303233Sdim
107303233Sdim  static CheckerProgramPointTag Tag("MPI-Checker", "MissingWait");
108303233Sdim  ExplodedNode *ErrorNode{nullptr};
109303233Sdim
110303233Sdim  auto ReqMap = State->get<RequestMap>();
111303233Sdim  for (const auto &Req : ReqMap) {
112303233Sdim    if (!SymReaper.isLiveRegion(Req.first)) {
113303233Sdim      if (Req.second.CurrentState == Request::State::Nonblocking) {
114303233Sdim
115303233Sdim        if (!ErrorNode) {
116303233Sdim          ErrorNode = Ctx.generateNonFatalErrorNode(State, &Tag);
117303233Sdim          State = ErrorNode->getState();
118303233Sdim        }
119314564Sdim        BReporter.reportMissingWait(Req.second, Req.first, ErrorNode,
120314564Sdim                                    Ctx.getBugReporter());
121303233Sdim      }
122303233Sdim      State = State->remove<RequestMap>(Req.first);
123303233Sdim    }
124303233Sdim  }
125303233Sdim
126303233Sdim  // Transition to update the state regarding removed requests.
127303233Sdim  if (!ErrorNode) {
128303233Sdim    Ctx.addTransition(State);
129303233Sdim  } else {
130303233Sdim    Ctx.addTransition(State, ErrorNode);
131303233Sdim  }
132303233Sdim}
133303233Sdim
134303233Sdimconst MemRegion *MPIChecker::topRegionUsedByWait(const CallEvent &CE) const {
135303233Sdim
136303233Sdim  if (FuncClassifier->isMPI_Wait(CE.getCalleeIdentifier())) {
137303233Sdim    return CE.getArgSVal(0).getAsRegion();
138303233Sdim  } else if (FuncClassifier->isMPI_Waitall(CE.getCalleeIdentifier())) {
139303233Sdim    return CE.getArgSVal(1).getAsRegion();
140303233Sdim  } else {
141303233Sdim    return (const MemRegion *)nullptr;
142303233Sdim  }
143303233Sdim}
144303233Sdim
145303233Sdimvoid MPIChecker::allRegionsUsedByWait(
146303233Sdim    llvm::SmallVector<const MemRegion *, 2> &ReqRegions,
147303233Sdim    const MemRegion *const MR, const CallEvent &CE, CheckerContext &Ctx) const {
148303233Sdim
149303233Sdim  MemRegionManager *const RegionManager = MR->getMemRegionManager();
150303233Sdim
151303233Sdim  if (FuncClassifier->isMPI_Waitall(CE.getCalleeIdentifier())) {
152321369Sdim    const SubRegion *SuperRegion{nullptr};
153303233Sdim    if (const ElementRegion *const ER = MR->getAs<ElementRegion>()) {
154321369Sdim      SuperRegion = cast<SubRegion>(ER->getSuperRegion());
155303233Sdim    }
156303233Sdim
157303233Sdim    // A single request is passed to MPI_Waitall.
158303233Sdim    if (!SuperRegion) {
159303233Sdim      ReqRegions.push_back(MR);
160303233Sdim      return;
161303233Sdim    }
162303233Sdim
163303233Sdim    const auto &Size = Ctx.getStoreManager().getSizeInElements(
164303233Sdim        Ctx.getState(), SuperRegion,
165303233Sdim        CE.getArgExpr(1)->getType()->getPointeeType());
166303233Sdim    const llvm::APSInt &ArrSize = Size.getAs<nonloc::ConcreteInt>()->getValue();
167303233Sdim
168303233Sdim    for (size_t i = 0; i < ArrSize; ++i) {
169303233Sdim      const NonLoc Idx = Ctx.getSValBuilder().makeArrayIndex(i);
170303233Sdim
171303233Sdim      const ElementRegion *const ER = RegionManager->getElementRegion(
172303233Sdim          CE.getArgExpr(1)->getType()->getPointeeType(), Idx, SuperRegion,
173303233Sdim          Ctx.getASTContext());
174303233Sdim
175303233Sdim      ReqRegions.push_back(ER->getAs<MemRegion>());
176303233Sdim    }
177303233Sdim  } else if (FuncClassifier->isMPI_Wait(CE.getCalleeIdentifier())) {
178303233Sdim    ReqRegions.push_back(MR);
179303233Sdim  }
180303233Sdim}
181303233Sdim
182303233Sdim} // end of namespace: mpi
183303233Sdim} // end of namespace: ento
184303233Sdim} // end of namespace: clang
185303233Sdim
186303233Sdim// Registers the checker for static analysis.
187303233Sdimvoid clang::ento::registerMPIChecker(CheckerManager &MGR) {
188303233Sdim  MGR.registerChecker<clang::ento::mpi::MPIChecker>();
189303233Sdim}
190353358Sdim
191353358Sdimbool clang::ento::shouldRegisterMPIChecker(const LangOptions &LO) {
192353358Sdim  return true;
193353358Sdim}
194