Index: clang-tidy/readability/CMakeLists.txt =================================================================== --- clang-tidy/readability/CMakeLists.txt +++ clang-tidy/readability/CMakeLists.txt @@ -11,6 +11,7 @@ IdentifierNamingCheck.cpp ImplicitBoolConversionCheck.cpp InconsistentDeclarationParameterNameCheck.cpp + SIMDIntrinsicsCheck.cpp MisleadingIndentationCheck.cpp MisplacedArrayIndexCheck.cpp NamedParameterCheck.cpp Index: clang-tidy/readability/ReadabilityTidyModule.cpp =================================================================== --- clang-tidy/readability/ReadabilityTidyModule.cpp +++ clang-tidy/readability/ReadabilityTidyModule.cpp @@ -31,6 +31,7 @@ #include "RedundantSmartptrGetCheck.h" #include "RedundantStringCStrCheck.h" #include "RedundantStringInitCheck.h" +#include "SIMDIntrinsicsCheck.h" #include "SimplifyBooleanExprCheck.h" #include "StaticAccessedThroughInstanceCheck.h" #include "StaticDefinitionInAnonymousNamespaceCheck.h" @@ -92,6 +93,8 @@ "readability-redundant-string-cstr"); CheckFactories.registerCheck( "readability-redundant-string-init"); + CheckFactories.registerCheck( + "readability-simd-intrinsics"); CheckFactories.registerCheck( "readability-simplify-boolean-expr"); CheckFactories.registerCheck( Index: clang-tidy/readability/SIMDIntrinsicsCheck.h =================================================================== --- /dev/null +++ clang-tidy/readability/SIMDIntrinsicsCheck.h @@ -0,0 +1,37 @@ +//===--- SIMDIntrinsicsCheck.h - clang-tidy----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_SIMD_INTRINSICS_CHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_SIMD_INTRINSICS_CHECK_H + +#include "../ClangTidy.h" + +namespace clang { +namespace tidy { +namespace readability { + +/// Find SIMD intrinsics calls and suggest std::experimental::simd alternatives. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/readability-simd-intrinsics.html +class SIMDIntrinsicsCheck : public ClangTidyCheck { +public: + SIMDIntrinsicsCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + + private: +}; + +} // namespace readability +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_SIMD_INTRINSICS_CHECK_H Index: clang-tidy/readability/SIMDIntrinsicsCheck.cpp =================================================================== --- /dev/null +++ clang-tidy/readability/SIMDIntrinsicsCheck.cpp @@ -0,0 +1,127 @@ +//===--- SIMDIntrinsicsCheck.cpp - clang-tidy------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "SIMDIntrinsicsCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/Triple.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace readability { + +namespace { + +StringRef CheckPPC(StringRef Name) { + if (Name.startswith("vec_")) + Name = Name.substr(4); + else + return ""; + + static const llvm::StringMap Mapping{ + // [simd.alg] + {"max", "std::experimental::simd::max"}, + {"min", "std::experimental::simd::min"}, + + // [simd.binary] + {"add", "std::experimental::simd::operator+"}, + {"sub", "std::experimental::simd::operator-"}, + {"mul", "std::experimental::simd::operator*"}, + }; + + auto It = Mapping.find(Name); + if (It != Mapping.end()) + return It->second; + return ""; +} + +StringRef CheckX86(StringRef Name) { + if (Name.startswith("_mm_")) + Name = Name.substr(4); + else if (Name.startswith("_mm256_")) + Name = Name.substr(7); + else if (Name.startswith("_mm512_")) + Name = Name.substr(7); + else + return ""; + + // [simd.alg] + if (Name.startswith("max_")) + return "std::experimental::simd::max"; + if (Name.startswith("min_")) + return "std::experimental::simd::min"; + + // [simd.binary] + if (Name.startswith("add_")) + return "std::experimental::simd::operator+"; + if (Name.startswith("sub_")) + return "std::experimental::simd::operator-"; + if (Name.startswith("mul_")) + return "std::experimental::simd::operator*"; + + return ""; +} + +} // namespace + +void SIMDIntrinsicsCheck::registerMatchers(MatchFinder *Finder) { + Finder->addMatcher( + callExpr(callee(functionDecl(matchesName("^::(_mm_|_mm256_|_mm512_|vec_)"))), + unless(isExpansionInSystemHeader())) + .bind("call"), + this); +} + +void SIMDIntrinsicsCheck::check(const MatchFinder::MatchResult &Result) { + const auto *Call = Result.Nodes.getNodeAs("call"); + assert(Call != nullptr); + + // If the callee has a VectorType parameter, we consider it a candidate of a + // SIMD intrinsic call. + const FunctionDecl *Callee = Call->getDirectCallee(); + if (!Callee) + return; + bool IsVector = false; + for (const ParmVarDecl *Parm : Callee->parameters()) + if (Parm->getType()->isVectorType()) + IsVector = true; + if (!IsVector) + return; + + StringRef Old = Callee->getName(); + StringRef New; + + llvm::Triple::ArchType Arch = + Result.Context->getTargetInfo().getTriple().getArch(); + switch (Arch) { + default: + break; + case llvm::Triple::ppc: + case llvm::Triple::ppc64: + case llvm::Triple::ppc64le: + New = CheckPPC(Old); + break; + case llvm::Triple::x86: + case llvm::Triple::x86_64: + New = CheckX86(Old); + break; + } + + if (!New.empty()) { + std::string Message = (Twine(Old) + " can be replaced by " + New).str(); + diag(Call->getExprLoc(), Message); + } +} + +} // namespace readability +} // namespace tidy +} // namespace clang Index: docs/clang-tidy/checks/list.rst =================================================================== --- docs/clang-tidy/checks/list.rst +++ docs/clang-tidy/checks/list.rst @@ -216,6 +216,7 @@ readability-redundant-smartptr-get readability-redundant-string-cstr readability-redundant-string-init + readability-simd-intrinsics readability-simplify-boolean-expr readability-static-accessed-through-instance readability-static-definition-in-anonymous-namespace Index: docs/clang-tidy/checks/readability-simd-intrinsics.rst =================================================================== --- /dev/null +++ docs/clang-tidy/checks/readability-simd-intrinsics.rst @@ -0,0 +1,32 @@ +.. title:: clang-tidy - simd-readability-intrinsics + +simd-readability-intrinsics +=========================== + +Finds SIMD intrinsics calls and suggests ``std::experimental::simd`` (`P0214`_) alternatives. + +For + +.. code-block:: c++ + + _mm_add_epi32(a, b); + +the check suggests an alternative: + +.. code-block:: c++ + + simd::experimental::simd::operator+ + +Many architectures provide SIMD operations (e.g. x86 SSE/AVX, Power AltiVec/VSX, +ARM NEON). It is common that SIMD code implementing the same algorithm, is +written in multiple target-dispatching pieces to optimize for different +architectures or micro-architectures. + +The C++ standard proposal `P0214`_ and its extensions cover many common SIMD +operations. By migrating from target-dependent intrinsics to `P0214` operations, +the SIMD code can be simplified and pieces for different targets can be unified. + +Refer to `P0214`_ for introduction and motivation for the data-parallel standard +library. + +.. _P0214: http://wg21.link/p0214