mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-01-09 01:29:52 +00:00
[clang-tidy] Add readability-simd-intrinsics
check.
Summary: 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 http://wg21.link/p0214 for introduction and motivation for the data-parallel standard library. Subscribers: klimek, aemerson, mgorny, xazax.hun, kristof.beyls, hintonda, cfe-commits Differential Revision: https://reviews.llvm.org/D42983 llvm-svn: 325272
This commit is contained in:
parent
b2c508b43f
commit
258a959084
@ -24,6 +24,7 @@ add_clang_library(clangTidyReadabilityModule
|
||||
RedundantStringCStrCheck.cpp
|
||||
RedundantSmartptrGetCheck.cpp
|
||||
RedundantStringInitCheck.cpp
|
||||
SIMDIntrinsicsCheck.cpp
|
||||
SimplifyBooleanExprCheck.cpp
|
||||
StaticAccessedThroughInstanceCheck.cpp
|
||||
StaticDefinitionInAnonymousNamespaceCheck.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 @@ public:
|
||||
"readability-redundant-string-cstr");
|
||||
CheckFactories.registerCheck<RedundantStringInitCheck>(
|
||||
"readability-redundant-string-init");
|
||||
CheckFactories.registerCheck<SIMDIntrinsicsCheck>(
|
||||
"readability-simd-intrinsics");
|
||||
CheckFactories.registerCheck<SimplifyBooleanExprCheck>(
|
||||
"readability-simplify-boolean-expr");
|
||||
CheckFactories.registerCheck<UniqueptrDeleteReleaseCheck>(
|
||||
|
152
clang-tools-extra/clang-tidy/readability/SIMDIntrinsicsCheck.cpp
Normal file
152
clang-tools-extra/clang-tidy/readability/SIMDIntrinsicsCheck.cpp
Normal file
@ -0,0 +1,152 @@
|
||||
//===--- 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"
|
||||
#include "llvm/Support/Regex.h"
|
||||
|
||||
using namespace clang::ast_matchers;
|
||||
|
||||
namespace clang {
|
||||
namespace tidy {
|
||||
namespace readability {
|
||||
|
||||
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<StringRef> 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), Suggest(Options.get("Suggest", 0) != 0) {}
|
||||
|
||||
void SIMDIntrinsicsCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
|
||||
Options.store(Opts, "Suggest", 0);
|
||||
}
|
||||
|
||||
void SIMDIntrinsicsCheck::registerMatchers(MatchFinder *Finder) {
|
||||
if (!getLangOpts().CPlusPlus11)
|
||||
return;
|
||||
// libcxx implementation backports it to C++11 std::experimental::simd.
|
||||
Std = getLangOpts().CPlusPlus2a ? "std" : "std::experimental";
|
||||
|
||||
Finder->addMatcher(callExpr(callee(functionDecl(allOf(
|
||||
matchesName("^::(_mm_|_mm256_|_mm512_|vec_)"),
|
||||
isVectorFunction()))),
|
||||
unless(isExpansionInSystemHeader()))
|
||||
.bind("call"),
|
||||
this);
|
||||
}
|
||||
|
||||
void SIMDIntrinsicsCheck::check(const MatchFinder::MatchResult &Result) {
|
||||
const auto *Call = Result.Nodes.getNodeAs<CallExpr>("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();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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", Message);
|
||||
} else {
|
||||
Message = (Twine("'") + Old + "' is a non-portable " +
|
||||
llvm::Triple::getArchTypeName(Arch) + " intrinsic function")
|
||||
.str();
|
||||
}
|
||||
diag(Call->getExprLoc(), Message);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace readability
|
||||
} // namespace tidy
|
||||
} // namespace clang
|
@ -0,0 +1,40 @@
|
||||
//===--- 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);
|
||||
|
||||
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
|
||||
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
|
||||
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
||||
|
||||
private:
|
||||
const bool Suggest;
|
||||
StringRef Std;
|
||||
};
|
||||
|
||||
} // namespace readability
|
||||
} // namespace tidy
|
||||
} // namespace clang
|
||||
|
||||
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_SIMD_INTRINSICS_CHECK_H
|
@ -88,6 +88,12 @@ Improvements to clang-tidy
|
||||
Functions that have trailing returns are disallowed, except for those
|
||||
using decltype specifiers and lambda with otherwise unutterable
|
||||
return types.
|
||||
|
||||
- New `readability-simd-intrinsics
|
||||
<http://clang.llvm.org/extra/clang-tidy/checks/readability-simd-intrinsics.html>`_ check
|
||||
|
||||
Warns if SIMD intrinsics are used which can be replaced by
|
||||
``std::experimental::simd`` operations.
|
||||
|
||||
- New alias `hicpp-avoid-goto
|
||||
<http://clang.llvm.org/extra/clang-tidy/checks/hicpp-avoid-goto.html>`_ to
|
||||
|
@ -217,6 +217,7 @@ Clang-Tidy Checks
|
||||
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
|
||||
|
@ -0,0 +1,42 @@
|
||||
.. title:: clang-tidy - readability-simd-intrinsics
|
||||
|
||||
readability-simd-intrinsics
|
||||
===========================
|
||||
|
||||
Finds SIMD intrinsics calls and suggests ``std::experimental::simd`` (`P0214`_) alternatives.
|
||||
|
||||
If the option ``Suggest`` is set to non-zero, for
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
_mm_add_epi32(a, b); // x86
|
||||
vec_add(a, b); // Power
|
||||
|
||||
the check suggests an alternative:
|
||||
|
||||
.. code-block::
|
||||
|
||||
operator+ on std::experimental::simd objects
|
||||
|
||||
Otherwise, it just complains the intrinsics are non-portable (and there are `P0214`_ alternatives).
|
||||
|
||||
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.
|
||||
|
||||
Options
|
||||
-------
|
||||
|
||||
.. option:: Suggest
|
||||
|
||||
If this option is set to non-zero (default is `0`), the check will suggest P0214 alternatives, otherwise it only points out the intrinsic function is non-portable.
|
||||
|
||||
.. _P0214: http://wg21.link/p0214
|
@ -0,0 +1,13 @@
|
||||
// RUN: %check_clang_tidy %s readability-simd-intrinsics %t -- \
|
||||
// RUN: -config='{CheckOptions: [ \
|
||||
// RUN: {key: readability-simd-intrinsics.Suggest, value: 1} \
|
||||
// RUN: ]}' -- -target ppc64le -maltivec -std=c++11
|
||||
|
||||
vector int vec_add(vector int, vector int);
|
||||
|
||||
void PPC() {
|
||||
vector int i0, i1;
|
||||
|
||||
vec_add(i0, i1);
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 'vec_add' can be replaced by operator+ on std::experimental::simd objects [readability-simd-intrinsics]
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
// RUN: %check_clang_tidy %s readability-simd-intrinsics %t -- \
|
||||
// RUN: -config='{CheckOptions: [ \
|
||||
// RUN: {key: readability-simd-intrinsics.Suggest, value: 1} \
|
||||
// RUN: ]}' -- -target x86_64 -std=c++11
|
||||
|
||||
typedef long long __m128i __attribute__((vector_size(16)));
|
||||
typedef double __m256 __attribute__((vector_size(32)));
|
||||
|
||||
__m128i _mm_add_epi32(__m128i, __m128i);
|
||||
__m256 _mm256_load_pd(double const *);
|
||||
void _mm256_store_pd(double *, __m256);
|
||||
|
||||
int _mm_add_fake(int, int);
|
||||
|
||||
void X86() {
|
||||
__m128i i0, i1;
|
||||
__m256 d0;
|
||||
|
||||
_mm_add_epi32(i0, i1);
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: '_mm_add_epi32' can be replaced by operator+ on std::experimental::simd objects [readability-simd-intrinsics]
|
||||
d0 = _mm256_load_pd(0);
|
||||
_mm256_store_pd(0, d0);
|
||||
|
||||
_mm_add_fake(0, 1);
|
||||
}
|
Loading…
Reference in New Issue
Block a user