1 //===--- SIMDIntrinsicsCheck.cpp - clang-tidy------------------------------===//
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-exception
7 //===----------------------------------------------------------------------===//
9 #include "SIMDIntrinsicsCheck.h"
10 #include "clang/AST/ASTContext.h"
11 #include "clang/ASTMatchers/ASTMatchFinder.h"
12 #include "clang/Basic/TargetInfo.h"
13 #include "llvm/ADT/StringMap.h"
14 #include "llvm/Support/ManagedStatic.h"
15 #include "llvm/Support/Regex.h"
16 #include "llvm/TargetParser/Triple.h"
18 using namespace clang::ast_matchers
;
20 namespace clang::tidy::portability
{
24 // If the callee has parameter of VectorType or pointer to VectorType,
25 // or the return type is VectorType, we consider it a vector function
26 // and a candidate for checking.
27 AST_MATCHER(FunctionDecl
, isVectorFunction
) {
28 bool IsVector
= Node
.getReturnType()->isVectorType();
29 for (const ParmVarDecl
*Parm
: Node
.parameters()) {
30 QualType Type
= Parm
->getType();
31 if (Type
->isPointerType())
32 Type
= Type
->getPointeeType();
33 if (Type
->isVectorType())
41 static StringRef
trySuggestPpc(StringRef Name
) {
42 if (!Name
.consume_front("vec_"))
45 return llvm::StringSwitch
<StringRef
>(Name
)
47 .Case("max", "$std::max")
48 .Case("min", "$std::min")
50 .Case("add", "operator+ on $simd objects")
51 .Case("sub", "operator- on $simd objects")
52 .Case("mul", "operator* on $simd objects")
56 static StringRef
trySuggestX86(StringRef Name
) {
57 if (!(Name
.consume_front("_mm_") || Name
.consume_front("_mm256_") ||
58 Name
.consume_front("_mm512_")))
62 if (Name
.starts_with("max_"))
64 if (Name
.starts_with("min_"))
68 if (Name
.starts_with("add_"))
69 return "operator+ on $simd objects";
70 if (Name
.starts_with("sub_"))
71 return "operator- on $simd objects";
72 if (Name
.starts_with("mul_"))
73 return "operator* on $simd objects";
78 SIMDIntrinsicsCheck::SIMDIntrinsicsCheck(StringRef Name
,
79 ClangTidyContext
*Context
)
80 : ClangTidyCheck(Name
, Context
), Std(Options
.get("Std", "")),
81 Suggest(Options
.get("Suggest", false)) {}
83 void SIMDIntrinsicsCheck::storeOptions(ClangTidyOptions::OptionMap
&Opts
) {
84 Options
.store(Opts
, "Std", Std
);
85 Options
.store(Opts
, "Suggest", Suggest
);
88 void SIMDIntrinsicsCheck::registerMatchers(MatchFinder
*Finder
) {
89 // If Std is not specified, infer it from the language options.
90 // libcxx implementation backports it to C++11 std::experimental::simd.
92 Std
= getLangOpts().CPlusPlus20
? "std" : "std::experimental";
94 Finder
->addMatcher(callExpr(callee(functionDecl(
95 matchesName("^::(_mm_|_mm256_|_mm512_|vec_)"),
97 unless(isExpansionInSystemHeader()))
102 void SIMDIntrinsicsCheck::check(const MatchFinder::MatchResult
&Result
) {
103 const auto *Call
= Result
.Nodes
.getNodeAs
<CallExpr
>("call");
104 assert(Call
!= nullptr);
105 const FunctionDecl
*Callee
= Call
->getDirectCallee();
109 StringRef Old
= Callee
->getName();
111 llvm::Triple::ArchType Arch
=
112 Result
.Context
->getTargetInfo().getTriple().getArch();
114 // We warn or suggest if this SIMD intrinsic function has a std::simd
119 case llvm::Triple::ppc
:
120 case llvm::Triple::ppc64
:
121 case llvm::Triple::ppc64le
:
122 New
= trySuggestPpc(Old
);
124 case llvm::Triple::x86
:
125 case llvm::Triple::x86_64
:
126 New
= trySuggestX86(Old
);
130 // We have found a std::simd replacement.
132 // If Suggest is true, give a P0214 alternative, otherwise point it out it
135 static const llvm::Regex
StdRegex("\\$std"), SimdRegex("\\$simd");
136 diag(Call
->getExprLoc(), "'%0' can be replaced by %1")
138 << SimdRegex
.sub(SmallString
<32>({Std
, "::simd"}),
139 StdRegex
.sub(Std
, New
));
141 diag(Call
->getExprLoc(), "'%0' is a non-portable %1 intrinsic function")
142 << Old
<< llvm::Triple::getArchTypeName(Arch
);
147 } // namespace clang::tidy::portability