//===--- SIMDIntrinsicsCheck.cpp - clang-tidy------------------------------===// // // 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 // //===----------------------------------------------------------------------===// #include "SIMDIntrinsicsCheck.h" #include "clang/AST/ASTContext.h" #include "clang/ASTMatchers/ASTMatchFinder.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/Triple.h" #include "llvm/Support/Regex.h" using namespace clang::ast_matchers; namespace clang { namespace tidy { namespace portability { namespace { // If the callee has parameter of VectorType or pointer to VectorType, // or the return type is VectorType, we consider it a vector function // and a candidate for checking. AST_MATCHER(FunctionDecl, isVectorFunction) { bool IsVector = Node.getReturnType()->isVectorType(); for (const ParmVarDecl *Parm : Node.parameters()) { QualType Type = Parm->getType(); if (Type->isPointerType()) Type = Type->getPointeeType(); if (Type->isVectorType()) IsVector = true; } return IsVector; } } // namespace static StringRef TrySuggestPPC(StringRef Name) { if (!Name.consume_front("vec_")) return {}; static const llvm::StringMap Mapping{ // [simd.alg] {"max", "$std::max"}, {"min", "$std::min"}, // [simd.binary] {"add", "operator+ on $simd objects"}, {"sub", "operator- on $simd objects"}, {"mul", "operator* on $simd objects"}, }; auto It = Mapping.find(Name); if (It != Mapping.end()) return It->second; return {}; } static StringRef TrySuggestX86(StringRef Name) { if (!(Name.consume_front("_mm_") || Name.consume_front("_mm256_") || Name.consume_front("_mm512_"))) return {}; // [simd.alg] if (Name.startswith("max_")) return "$simd::max"; if (Name.startswith("min_")) return "$simd::min"; // [simd.binary] if (Name.startswith("add_")) return "operator+ on $simd objects"; if (Name.startswith("sub_")) return "operator- on $simd objects"; if (Name.startswith("mul_")) return "operator* on $simd objects"; return {}; } SIMDIntrinsicsCheck::SIMDIntrinsicsCheck(StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context), Std(Options.get("Std", "")), Suggest(Options.get("Suggest", 0) != 0) {} void SIMDIntrinsicsCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { Options.store(Opts, "Std", ""); Options.store(Opts, "Suggest", 0); } void SIMDIntrinsicsCheck::registerMatchers(MatchFinder *Finder) { if (!getLangOpts().CPlusPlus11) return; // If Std is not specified, infer it from the language options. // libcxx implementation backports it to C++11 std::experimental::simd. if (Std.empty()) Std = getLangOpts().CPlusPlus2a ? "std" : "std::experimental"; Finder->addMatcher(callExpr(callee(functionDecl( matchesName("^::(_mm_|_mm256_|_mm512_|vec_)"), isVectorFunction())), unless(isExpansionInSystemHeader())) .bind("call"), this); } void SIMDIntrinsicsCheck::check(const MatchFinder::MatchResult &Result) { const auto *Call = Result.Nodes.getNodeAs("call"); assert(Call != nullptr); const FunctionDecl *Callee = Call->getDirectCallee(); if (!Callee) return; StringRef Old = Callee->getName(); StringRef New; llvm::Triple::ArchType Arch = Result.Context->getTargetInfo().getTriple().getArch(); // We warn or suggest if this SIMD intrinsic function has a std::simd // replacement. switch (Arch) { default: break; case llvm::Triple::ppc: case llvm::Triple::ppc64: case llvm::Triple::ppc64le: New = TrySuggestPPC(Old); break; case llvm::Triple::x86: case llvm::Triple::x86_64: New = TrySuggestX86(Old); break; } // We have found a std::simd replacement. if (!New.empty()) { std::string Message; // If Suggest is true, give a P0214 alternative, otherwise point it out it // is non-portable. if (Suggest) { Message = (Twine("'") + Old + "' can be replaced by " + New).str(); Message = llvm::Regex("\\$std").sub(Std, Message); Message = llvm::Regex("\\$simd").sub((Std.str() + "::simd").str(), Message); } else { Message = (Twine("'") + Old + "' is a non-portable " + llvm::Triple::getArchTypeName(Arch) + " intrinsic function") .str(); } diag(Call->getExprLoc(), Message); } } } // namespace portability } // namespace tidy } // namespace clang