/** * Check if communication between first, last rank. * * @return is matching */ bool MPICheckerAST::isFirstLastPair(const MPICall &sendCall, const MPICall &recvCall, const MPIRankCase &sendCase, const MPIRankCase &recvCase) const { const auto &rankArgSend = sendCall.arg(MPIPointToPoint::kRank); const auto &rankArgRecv = recvCall.arg(MPIPointToPoint::kRank); llvm::SmallVector<std::string, 1> firstRank{"0"}; llvm::SmallVector<std::string, 3> lastRank{"-", MPIProcessCount::encoding, "1"}; // first to last match if (sendCase.isFirstRank() && rankArgSend.valueSequence() == lastRank && recvCase.isLastRank() && rankArgRecv.valueSequence() == firstRank) { return true; } // last to first match else if (sendCase.isLastRank() && rankArgSend.valueSequence() == firstRank && recvCase.isFirstRank() && rankArgRecv.valueSequence() == lastRank) { return true; } else { return false; } }
/** * Check if two calls are a send/recv pair. * * @param sendCall * @param recvCall * * @return if they are send/recv pair */ bool MPICheckerAST::isSendRecvPair(const MPICall &sendCall, const MPICall &recvCall) const { if (!funcClassifier_.isSendType(sendCall)) return false; if (!funcClassifier_.isRecvType(recvCall)) return false; if (!areDatatypesEqual(sendCall, recvCall)) return false; // compare count, tag for (const size_t idx : {MPIPointToPoint::kCount, MPIPointToPoint::kTag}) { if (!sendCall.arguments()[idx].isEqual(recvCall.arguments()[idx])) { return false; } } // compare ranks const auto &rankArgSend = sendCall.arguments()[MPIPointToPoint::kRank]; const auto &rankArgRecv = recvCall.arguments()[MPIPointToPoint::kRank]; if (rankArgSend.typeSequence().size() != rankArgRecv.typeSequence().size()) return false; // build sequences without last operator(skip first element) std::vector<ArgumentVisitor::ComponentType> seq1, seq2; std::vector<std::string> val1, val2; bool containsSubtraction{false}; for (size_t i = 1; i < rankArgSend.typeSequence().size(); ++i) { seq1.push_back(rankArgSend.typeSequence()[i]); val1.push_back(rankArgSend.valueSequence()[i]); seq2.push_back(rankArgRecv.typeSequence()[i]); val2.push_back(rankArgRecv.valueSequence()[i]); if (rankArgSend.valueSequence()[i] == "-") containsSubtraction = true; if (rankArgRecv.valueSequence()[i] == "-") containsSubtraction = true; } if (containsSubtraction) { // check ordered if (seq1 != seq2 || val1 != val2) return false; } else { // check permutation if ((!cont::isPermutation(seq1, seq2)) || (!cont::isPermutation(val1, val2))) { return false; } } // last (value|var|function) must be identical if (val1.back() != val2.back()) return false; // last operator must be inverse if (!rankArgSend.isLastOperatorInverse(rankArgRecv)) return false; return true; }
/** * Checks if mpi-datatypes for 2 different point to point calls are equal. * * @param sendCall * @param recvCall * * @return equality */ bool MPICheckerAST::areDatatypesEqual(const MPICall &sendCall, const MPICall &recvCall) const { // compare mpi datatype llvm::StringRef sendDataType = util::sourceRangeAsStringRef( sendCall.arguments()[MPIPointToPoint::kDatatype] .stmt_->getSourceRange(), analysisManager_); llvm::StringRef recvDataType = util::sourceRangeAsStringRef( recvCall.arguments()[MPIPointToPoint::kDatatype] .stmt_->getSourceRange(), analysisManager_); return sendDataType == recvDataType; }
/** * Select apprioriate function to match the buffer type against * the specified mpi datatype. * * @param typeVisitor contains information about the buffer * @param mpiCall call whose arguments are observed * @param mpiDatatypeString * @param idxPair bufferIdx, mpiDatatypeIdx */ void MPICheckerAST::selectTypeMatcher( const mpi::TypeVisitor &typeVisitor, const MPICall &mpiCall, const StringRef mpiDatatypeString, const std::pair<size_t, size_t> &idxPair) const { const clang::BuiltinType *builtinType = typeVisitor.builtinType(); bool isTypeMatching{true}; // check for exact width types (e.g. int16_t, uint32_t) if (typeVisitor.isTypedefType()) { isTypeMatching = matchExactWidthType(typeVisitor, mpiDatatypeString); } // check for complex-floating types (e.g. float _Complex) else if (typeVisitor.isComplexType()) { isTypeMatching = matchComplexType(typeVisitor, mpiDatatypeString); } // check for basic builtin types (e.g. int, char) else if (!builtinType) { return; // if no builtin type cancel checking } else if (builtinType->isBooleanType()) { isTypeMatching = matchBoolType(typeVisitor, mpiDatatypeString); } else if (builtinType->isAnyCharacterType()) { isTypeMatching = matchCharType(typeVisitor, mpiDatatypeString); } else if (builtinType->isSignedInteger()) { isTypeMatching = matchSignedType(typeVisitor, mpiDatatypeString); } else if (builtinType->isUnsignedIntegerType()) { isTypeMatching = matchUnsignedType(typeVisitor, mpiDatatypeString); } else if (builtinType->isFloatingType()) { isTypeMatching = matchFloatType(typeVisitor, mpiDatatypeString); } if (!isTypeMatching) bugReporter_.reportTypeMismatch(mpiCall.callExpr(), idxPair, typeVisitor.qualType_, mpiDatatypeString); }
/** * Returns index pairs for each buffer, datatype pair. * * @param mpiCall * * @return index pairs */ MPICheckerAST::IndexPairs MPICheckerAST::bufferDataTypeIndices( const MPICall &mpiCall) const { IndexPairs indexPairs; if (funcClassifier_.isPointToPointType(mpiCall)) { indexPairs.push_back( {MPIPointToPoint::kBuf, MPIPointToPoint::kDatatype}); } else if (funcClassifier_.isCollectiveType(mpiCall)) { if (funcClassifier_.isReduceType(mpiCall)) { // only check buffer type if not inplace if (util::sourceRangeAsStringRef( mpiCall.callExpr()->getArg(0)->getSourceRange(), analysisManager_) != "MPI_IN_PLACE") { indexPairs.push_back({0, 3}); } indexPairs.push_back({1, 3}); } else if (funcClassifier_.isScatterType(mpiCall) || funcClassifier_.isGatherType(mpiCall) || funcClassifier_.isAlltoallType(mpiCall)) { indexPairs.push_back({0, 2}); indexPairs.push_back({3, 5}); } else if (funcClassifier_.isBcastType(mpiCall)) { indexPairs.push_back({0, 2}); } } return indexPairs; }
/** * Check if rank arguments of send, recv pair match. * * @return is matching */ bool MPICheckerAST::rankArgsMatch(const MPICall &sendCall, const MPICall &recvCall, const MPIRankCase &sendCase, const MPIRankCase &recvCase) const { // match special case if (isFirstLastPair(sendCall, recvCall, sendCase, recvCase)) return true; // compare ranks const auto &rankArgSend = sendCall.arg(MPIPointToPoint::kRank); const auto &rankArgRecv = recvCall.arg(MPIPointToPoint::kRank); if (rankArgSend.valueSequence().size() != rankArgRecv.valueSequence().size()) return false; // build sequences without last operator(skip first element) const llvm::SmallVector<std::string, 4> sendValSeq{ rankArgSend.valueSequence().begin() + 1, rankArgSend.valueSequence().end()}, recvValSeq{rankArgRecv.valueSequence().begin() + 1, rankArgRecv.valueSequence().end()}; bool containsSubtraction{false}; for (size_t i = 0; i < sendValSeq.size(); ++i) { if (sendValSeq[i] == "-" || recvValSeq[i] == "-") { containsSubtraction = true; break; } } // check ordered if (containsSubtraction && (sendValSeq != recvValSeq)) return false; // check permutation if (!containsSubtraction && (!cont::isPermutation(sendValSeq, recvValSeq))) { return false; } // last (value|var|function) must be identical if (sendValSeq.back() != recvValSeq.back()) return false; // last operator must be inverse if (!rankArgSend.isLastOperatorInverse(rankArgRecv)) return false; return true; }
/** * Checks if two (point to point) calls are a send/recv pair. * * @param sendCall * @param recvCall * * @return if they are send/recv pair */ bool MPICheckerAST::isSendRecvPair(const MPICall &sendCall, const MPICall &recvCall, const MPIRankCase &sendCase, const MPIRankCase &recvCase) const { if (!funcClassifier_.isSendType(sendCall)) return false; if (!funcClassifier_.isRecvType(recvCall)) return false; if (!areDatatypesEqual(sendCall, recvCall)) return false; // compare count, tag for (const size_t idx : { MPIPointToPoint::kCount, MPIPointToPoint::kTag }) { if (sendCall.arg(idx) != recvCall.arg(idx)) { return false; } } return rankArgsMatch(sendCall, recvCall, sendCase, recvCase); }
/** * Checks if buffer type and specified mpi datatype matches. * * @param mpiCall call to check type correspondence for */ void MPICheckerAST::checkBufferTypeMatch(const MPICall &mpiCall) const { // one pair consists of {bufferIdx, mpiDatatypeIdx} IndexPairs indexPairs = bufferDataTypeIndices(mpiCall); // for every buffer mpi-data pair in function // check if their types match for (const auto &idxPair : indexPairs) { const VarDecl *bufferArg = mpiCall.arguments()[idxPair.first].vars().front(); // collect buffer type information const mpi::TypeVisitor typeVisitor{bufferArg->getType()}; // get mpi datatype as string auto mpiDatatype = mpiCall.arguments()[idxPair.second].stmt_; StringRef mpiDatatypeString{util::sourceRangeAsStringRef( mpiDatatype->getSourceRange(), analysisManager_)}; selectTypeMatcher(typeVisitor, mpiCall, mpiDatatypeString, idxPair); } }
/** * Check if invalid argument types are used in a mpi call. * This check looks at indices where only integer values are valid. * (count, rank, tag) Any non integer type usage is reported. * * @param mpiCall to check the arguments for */ void MPICheckerAST::checkForInvalidArgs(const MPICall &mpiCall) const { std::vector<size_t> indicesToCheck = integerIndices(mpiCall); if (!indicesToCheck.size()) return; // iterate indices which should not have integer arguments for (const size_t idx : indicesToCheck) { // check for invalid variable types const auto &arg = mpiCall.arguments()[idx]; const auto &vars = arg.vars(); for (const auto &var : vars) { const mpi::TypeVisitor typeVisitor{var->getType()}; if (!typeVisitor.builtinType() || !typeVisitor.builtinType()->isIntegerType()) { bugReporter_.reportInvalidArgumentType( mpiCall.callExpr(), idx, var->getSourceRange(), "Variable"); } } // check for float literals if (arg.floatingLiterals().size()) { bugReporter_.reportInvalidArgumentType( mpiCall.callExpr(), idx, arg.floatingLiterals().front()->getSourceRange(), "Literal"); } // check for invalid return types from functions const auto &functions = arg.functions(); for (const auto &function : functions) { const mpi::TypeVisitor typeVisitor{function->getReturnType()}; if (!typeVisitor.builtinType() || !typeVisitor.builtinType()->isIntegerType()) { bugReporter_.reportInvalidArgumentType( mpiCall.callExpr(), idx, function->getSourceRange(), "Return value"); } } } }
/** * Check if there is a redundant call to the call passed. * * @param callToCheck */ void MPICheckerAST::checkForRedundantCall(const MPICall &callToCheck, const MPIRankCase &rankCase) const { for (const MPICall &comparedCall : rankCase.mpiCalls()) { if (qualifyRedundancyCheck(callToCheck, comparedCall)) { if (callToCheck == comparedCall) { bugReporter_.reportRedundantCall(callToCheck.callExpr(), comparedCall.callExpr()); callToCheck.isMarked_ = true; callToCheck.isMarked_ = true; } } } return; }
/** * Check if two calls qualify for a redundancy check. * * @param callToCheck * @param comparedCall * * @return */ bool MPICheckerAST::qualifyRedundancyCheck(const MPICall &callToCheck, const MPICall &comparedCall) const { if (comparedCall.isMarked_) return false; // to omit double matching // do not compare with the call itself if (callToCheck.id() == comparedCall.id()) return false; if (!((funcClassifier_.isPointToPointType(callToCheck) && funcClassifier_.isPointToPointType(comparedCall)) || (funcClassifier_.isCollectiveType(callToCheck) && funcClassifier_.isCollectiveType(comparedCall)))) return false; if (funcClassifier_.isPointToPointType(callToCheck)) { // both must be send or recv types return (funcClassifier_.isSendType(callToCheck) && funcClassifier_.isSendType(comparedCall)) || (funcClassifier_.isRecvType(callToCheck) && funcClassifier_.isRecvType(comparedCall)); } else if (funcClassifier_.isCollectiveType(callToCheck)) { // calls must be of the same type return (funcClassifier_.isScatterType(callToCheck) && funcClassifier_.isScatterType(comparedCall)) || (funcClassifier_.isGatherType(callToCheck) && funcClassifier_.isGatherType(comparedCall)) || (funcClassifier_.isAlltoallType(callToCheck) && funcClassifier_.isAlltoallType(comparedCall)) || (funcClassifier_.isBcastType(callToCheck) && funcClassifier_.isBcastType(comparedCall)) || (funcClassifier_.isReduceType(callToCheck) && funcClassifier_.isReduceType(comparedCall)); } return false; }