332 lines · cpp
1//===----------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "TypeMismatchCheck.h"10#include "clang/Lex/Lexer.h"11#include "clang/Tooling/FixIt.h"12#include "llvm/ADT/StringSet.h"13#include <map>14 15using namespace clang::ast_matchers;16 17namespace clang::tidy::mpi {18 19/// Check if a BuiltinType::Kind matches the MPI datatype.20///21/// \param MultiMap datatype group22/// \param Kind buffer type kind23/// \param MPIDatatype name of the MPI datatype24///25/// \returns true if the pair matches26static bool27isMPITypeMatching(const std::multimap<BuiltinType::Kind, StringRef> &MultiMap,28 const BuiltinType::Kind Kind, StringRef MPIDatatype) {29 auto ItPair = MultiMap.equal_range(Kind);30 while (ItPair.first != ItPair.second) {31 if (ItPair.first->second == MPIDatatype)32 return true;33 ++ItPair.first;34 }35 return false;36}37 38/// Check if the MPI datatype is a standard type.39///40/// \param MPIDatatype name of the MPI datatype41///42/// \returns true if the type is a standard type43static bool isStandardMPIDatatype(StringRef MPIDatatype) {44 static const llvm::StringSet<> AllTypes = {"MPI_C_BOOL",45 "MPI_CHAR",46 "MPI_SIGNED_CHAR",47 "MPI_UNSIGNED_CHAR",48 "MPI_WCHAR",49 "MPI_INT",50 "MPI_LONG",51 "MPI_SHORT",52 "MPI_LONG_LONG",53 "MPI_LONG_LONG_INT",54 "MPI_UNSIGNED",55 "MPI_UNSIGNED_SHORT",56 "MPI_UNSIGNED_LONG",57 "MPI_UNSIGNED_LONG_LONG",58 "MPI_FLOAT",59 "MPI_DOUBLE",60 "MPI_LONG_DOUBLE",61 "MPI_C_COMPLEX",62 "MPI_C_FLOAT_COMPLEX",63 "MPI_C_DOUBLE_COMPLEX",64 "MPI_C_LONG_DOUBLE_COMPLEX",65 "MPI_INT8_T",66 "MPI_INT16_T",67 "MPI_INT32_T",68 "MPI_INT64_T",69 "MPI_UINT8_T",70 "MPI_UINT16_T",71 "MPI_UINT32_T",72 "MPI_UINT64_T",73 "MPI_CXX_BOOL",74 "MPI_CXX_FLOAT_COMPLEX",75 "MPI_CXX_DOUBLE_COMPLEX",76 "MPI_CXX_LONG_DOUBLE_COMPLEX"};77 78 return AllTypes.contains(MPIDatatype);79}80 81/// Check if a BuiltinType matches the MPI datatype.82///83/// \param Builtin the builtin type84/// \param BufferTypeName buffer type name, gets assigned85/// \param MPIDatatype name of the MPI datatype86/// \param LO language options87///88/// \returns true if the type matches89static bool isBuiltinTypeMatching(const BuiltinType *Builtin,90 std::string &BufferTypeName,91 StringRef MPIDatatype,92 const LangOptions &LO) {93 static const std::multimap<BuiltinType::Kind, StringRef> BuiltinMatches = {94 // On some systems like PPC or ARM, 'char' is unsigned by default which is95 // why distinct signedness for the buffer and MPI type is tolerated.96 {BuiltinType::SChar, "MPI_CHAR"},97 {BuiltinType::SChar, "MPI_SIGNED_CHAR"},98 {BuiltinType::SChar, "MPI_UNSIGNED_CHAR"},99 {BuiltinType::Char_S, "MPI_CHAR"},100 {BuiltinType::Char_S, "MPI_SIGNED_CHAR"},101 {BuiltinType::Char_S, "MPI_UNSIGNED_CHAR"},102 {BuiltinType::UChar, "MPI_CHAR"},103 {BuiltinType::UChar, "MPI_SIGNED_CHAR"},104 {BuiltinType::UChar, "MPI_UNSIGNED_CHAR"},105 {BuiltinType::Char_U, "MPI_CHAR"},106 {BuiltinType::Char_U, "MPI_SIGNED_CHAR"},107 {BuiltinType::Char_U, "MPI_UNSIGNED_CHAR"},108 {BuiltinType::WChar_S, "MPI_WCHAR"},109 {BuiltinType::WChar_U, "MPI_WCHAR"},110 {BuiltinType::Bool, "MPI_C_BOOL"},111 {BuiltinType::Bool, "MPI_CXX_BOOL"},112 {BuiltinType::Short, "MPI_SHORT"},113 {BuiltinType::Int, "MPI_INT"},114 {BuiltinType::Long, "MPI_LONG"},115 {BuiltinType::LongLong, "MPI_LONG_LONG"},116 {BuiltinType::LongLong, "MPI_LONG_LONG_INT"},117 {BuiltinType::UShort, "MPI_UNSIGNED_SHORT"},118 {BuiltinType::UInt, "MPI_UNSIGNED"},119 {BuiltinType::ULong, "MPI_UNSIGNED_LONG"},120 {BuiltinType::ULongLong, "MPI_UNSIGNED_LONG_LONG"},121 {BuiltinType::Float, "MPI_FLOAT"},122 {BuiltinType::Double, "MPI_DOUBLE"},123 {BuiltinType::LongDouble, "MPI_LONG_DOUBLE"}};124 125 if (!isMPITypeMatching(BuiltinMatches, Builtin->getKind(), MPIDatatype)) {126 BufferTypeName = std::string(Builtin->getName(LO));127 return false;128 }129 130 return true;131}132 133/// Check if a complex float/double/long double buffer type matches134/// the MPI datatype.135///136/// \param Complex buffer type137/// \param BufferTypeName buffer type name, gets assigned138/// \param MPIDatatype name of the MPI datatype139/// \param LO language options140///141/// \returns true if the type matches or the buffer type is unknown142static bool isCComplexTypeMatching(const ComplexType *const Complex,143 std::string &BufferTypeName,144 StringRef MPIDatatype,145 const LangOptions &LO) {146 static const std::multimap<BuiltinType::Kind, StringRef> ComplexCMatches = {147 {BuiltinType::Float, "MPI_C_COMPLEX"},148 {BuiltinType::Float, "MPI_C_FLOAT_COMPLEX"},149 {BuiltinType::Double, "MPI_C_DOUBLE_COMPLEX"},150 {BuiltinType::LongDouble, "MPI_C_LONG_DOUBLE_COMPLEX"}};151 152 const auto *Builtin =153 Complex->getElementType().getTypePtr()->getAs<BuiltinType>();154 155 if (Builtin &&156 !isMPITypeMatching(ComplexCMatches, Builtin->getKind(), MPIDatatype)) {157 BufferTypeName = (llvm::Twine(Builtin->getName(LO)) + " _Complex").str();158 return false;159 }160 return true;161}162 163/// Check if a complex<float/double/long double> templated buffer type matches164/// the MPI datatype.165///166/// \param Template buffer type167/// \param BufferTypeName buffer type name, gets assigned168/// \param MPIDatatype name of the MPI datatype169/// \param LO language options170///171/// \returns true if the type matches or the buffer type is unknown172static bool173isCXXComplexTypeMatching(const TemplateSpecializationType *const Template,174 std::string &BufferTypeName, StringRef MPIDatatype,175 const LangOptions &LO) {176 static const std::multimap<BuiltinType::Kind, StringRef> ComplexCXXMatches = {177 {BuiltinType::Float, "MPI_CXX_FLOAT_COMPLEX"},178 {BuiltinType::Double, "MPI_CXX_DOUBLE_COMPLEX"},179 {BuiltinType::LongDouble, "MPI_CXX_LONG_DOUBLE_COMPLEX"}};180 181 if (Template->getAsCXXRecordDecl()->getName() != "complex")182 return true;183 184 const auto *Builtin = Template->template_arguments()[0]185 .getAsType()186 .getTypePtr()187 ->getAs<BuiltinType>();188 189 if (Builtin &&190 !isMPITypeMatching(ComplexCXXMatches, Builtin->getKind(), MPIDatatype)) {191 BufferTypeName =192 (llvm::Twine("complex<") + Builtin->getName(LO) + ">").str();193 return false;194 }195 196 return true;197}198 199/// Check if a fixed size width buffer type matches the MPI datatype.200///201/// \param Typedef buffer type202/// \param BufferTypeName buffer type name, gets assigned203/// \param MPIDatatype name of the MPI datatype204///205/// \returns true if the type matches or the buffer type is unknown206static bool isTypedefTypeMatching(const TypedefType *const Typedef,207 std::string &BufferTypeName,208 StringRef MPIDatatype) {209 static llvm::StringMap<StringRef> FixedWidthMatches = {210 {"int8_t", "MPI_INT8_T"}, {"int16_t", "MPI_INT16_T"},211 {"int32_t", "MPI_INT32_T"}, {"int64_t", "MPI_INT64_T"},212 {"uint8_t", "MPI_UINT8_T"}, {"uint16_t", "MPI_UINT16_T"},213 {"uint32_t", "MPI_UINT32_T"}, {"uint64_t", "MPI_UINT64_T"}};214 215 const auto It = FixedWidthMatches.find(Typedef->getDecl()->getName());216 // Check if the typedef is known and not matching the MPI datatype.217 if (It != FixedWidthMatches.end() && It->getValue() != MPIDatatype) {218 BufferTypeName = std::string(Typedef->getDecl()->getName());219 return false;220 }221 return true;222}223 224/// Get the unqualified, dereferenced type of an argument.225///226/// \param CE call expression227/// \param Idx argument index228///229/// \returns type of the argument230static const Type *argumentType(const CallExpr *const CE, const size_t Idx) {231 const QualType QT = CE->getArg(Idx)->IgnoreImpCasts()->getType();232 return QT.getTypePtr()->getPointeeOrArrayElementType();233}234 235void TypeMismatchCheck::registerMatchers(MatchFinder *Finder) {236 Finder->addMatcher(callExpr().bind("CE"), this);237}238 239void TypeMismatchCheck::check(const MatchFinder::MatchResult &Result) {240 const auto *const CE = Result.Nodes.getNodeAs<CallExpr>("CE");241 if (!CE->getDirectCallee())242 return;243 244 if (!FuncClassifier)245 FuncClassifier.emplace(*Result.Context);246 247 const IdentifierInfo *Identifier = CE->getDirectCallee()->getIdentifier();248 if (!Identifier || !FuncClassifier->isMPIType(Identifier))249 return;250 251 // These containers are used, to capture buffer, MPI datatype pairs.252 SmallVector<const Type *, 1> BufferTypes;253 SmallVector<const Expr *, 1> BufferExprs;254 SmallVector<StringRef, 1> MPIDatatypes;255 256 // Adds a buffer, MPI datatype pair of an MPI call expression to the257 // containers. For buffers, the type and expression is captured.258 auto AddPair = [&CE, &Result, &BufferTypes, &BufferExprs, &MPIDatatypes](259 const size_t BufferIdx, const size_t DatatypeIdx) {260 // Skip null pointer constants and in place 'operators'.261 if (CE->getArg(BufferIdx)->isNullPointerConstant(262 *Result.Context, Expr::NPC_ValueDependentIsNull) ||263 tooling::fixit::getText(*CE->getArg(BufferIdx), *Result.Context) ==264 "MPI_IN_PLACE")265 return;266 267 const StringRef MPIDatatype =268 tooling::fixit::getText(*CE->getArg(DatatypeIdx), *Result.Context);269 270 const Type *ArgType = argumentType(CE, BufferIdx);271 // Skip unknown MPI datatypes and void pointers.272 if (!isStandardMPIDatatype(MPIDatatype) || ArgType->isVoidType())273 return;274 275 BufferTypes.push_back(ArgType);276 BufferExprs.push_back(CE->getArg(BufferIdx));277 MPIDatatypes.push_back(MPIDatatype);278 };279 280 // Collect all buffer, MPI datatype pairs for the inspected call expression.281 if (FuncClassifier->isPointToPointType(Identifier)) {282 AddPair(0, 2);283 } else if (FuncClassifier->isCollectiveType(Identifier)) {284 if (FuncClassifier->isReduceType(Identifier)) {285 AddPair(0, 3);286 AddPair(1, 3);287 } else if (FuncClassifier->isScatterType(Identifier) ||288 FuncClassifier->isGatherType(Identifier) ||289 FuncClassifier->isAlltoallType(Identifier)) {290 AddPair(0, 2);291 AddPair(3, 5);292 } else if (FuncClassifier->isBcastType(Identifier)) {293 AddPair(0, 2);294 }295 }296 checkArguments(BufferTypes, BufferExprs, MPIDatatypes, getLangOpts());297}298 299void TypeMismatchCheck::checkArguments(ArrayRef<const Type *> BufferTypes,300 ArrayRef<const Expr *> BufferExprs,301 ArrayRef<StringRef> MPIDatatypes,302 const LangOptions &LO) {303 std::string BufferTypeName;304 305 for (size_t I = 0; I < MPIDatatypes.size(); ++I) {306 const Type *const BT = BufferTypes[I];307 bool Error = false;308 309 if (const auto *Typedef = BT->getAs<TypedefType>()) {310 Error = !isTypedefTypeMatching(Typedef, BufferTypeName, MPIDatatypes[I]);311 } else if (const auto *Complex = BT->getAs<ComplexType>()) {312 Error =313 !isCComplexTypeMatching(Complex, BufferTypeName, MPIDatatypes[I], LO);314 } else if (const auto *Template = BT->getAs<TemplateSpecializationType>()) {315 Error = !isCXXComplexTypeMatching(Template, BufferTypeName,316 MPIDatatypes[I], LO);317 } else if (const auto *Builtin = BT->getAs<BuiltinType>()) {318 Error =319 !isBuiltinTypeMatching(Builtin, BufferTypeName, MPIDatatypes[I], LO);320 }321 322 if (Error) {323 const auto Loc = BufferExprs[I]->getSourceRange().getBegin();324 diag(Loc, "buffer type '%0' does not match the MPI datatype '%1'")325 << BufferTypeName << MPIDatatypes[I];326 }327 }328}329 330void TypeMismatchCheck::onEndOfTranslationUnit() { FuncClassifier.reset(); }331} // namespace clang::tidy::mpi332