blob: d5434cc2e6caaba177878d8c3c2b2f1d67bccb12 [file] [log] [blame]
Fangrui Song258a9592018-02-15 17:56:43 +00001//===--- SIMDIntrinsicsCheck.cpp - clang-tidy------------------------------===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10#include "SIMDIntrinsicsCheck.h"
11#include "clang/AST/ASTContext.h"
12#include "clang/ASTMatchers/ASTMatchFinder.h"
13#include "llvm/ADT/StringMap.h"
14#include "llvm/ADT/Triple.h"
15#include "llvm/Support/Regex.h"
16
17using namespace clang::ast_matchers;
18
19namespace clang {
20namespace tidy {
Fangrui Songc0e768d2018-03-07 16:57:42 +000021namespace portability {
Fangrui Song258a9592018-02-15 17:56:43 +000022
23namespace {
24
25// If the callee has parameter of VectorType or pointer to VectorType,
26// or the return type is VectorType, we consider it a vector function
27// and a candidate for checking.
28AST_MATCHER(FunctionDecl, isVectorFunction) {
29 bool IsVector = Node.getReturnType()->isVectorType();
30 for (const ParmVarDecl *Parm : Node.parameters()) {
31 QualType Type = Parm->getType();
32 if (Type->isPointerType())
33 Type = Type->getPointeeType();
34 if (Type->isVectorType())
35 IsVector = true;
36 }
37 return IsVector;
38}
39
40} // namespace
41
42static StringRef TrySuggestPPC(StringRef Name) {
43 if (!Name.consume_front("vec_"))
44 return {};
45
46 static const llvm::StringMap<StringRef> Mapping{
47 // [simd.alg]
48 {"max", "$std::max"},
49 {"min", "$std::min"},
50
51 // [simd.binary]
52 {"add", "operator+ on $simd objects"},
53 {"sub", "operator- on $simd objects"},
54 {"mul", "operator* on $simd objects"},
55 };
56
57 auto It = Mapping.find(Name);
58 if (It != Mapping.end())
59 return It->second;
60 return {};
61}
62
63static StringRef TrySuggestX86(StringRef Name) {
64 if (!(Name.consume_front("_mm_") || Name.consume_front("_mm256_") ||
65 Name.consume_front("_mm512_")))
66 return {};
67
68 // [simd.alg]
69 if (Name.startswith("max_"))
70 return "$simd::max";
71 if (Name.startswith("min_"))
72 return "$simd::min";
73
74 // [simd.binary]
75 if (Name.startswith("add_"))
76 return "operator+ on $simd objects";
77 if (Name.startswith("sub_"))
78 return "operator- on $simd objects";
79 if (Name.startswith("mul_"))
80 return "operator* on $simd objects";
81
82 return {};
83}
84
85SIMDIntrinsicsCheck::SIMDIntrinsicsCheck(StringRef Name,
86 ClangTidyContext *Context)
Fangrui Songc0e768d2018-03-07 16:57:42 +000087 : ClangTidyCheck(Name, Context), Std(Options.get("Std", "")),
88 Suggest(Options.get("Suggest", 0) != 0) {}
Fangrui Song258a9592018-02-15 17:56:43 +000089
90void SIMDIntrinsicsCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
Fangrui Songc0e768d2018-03-07 16:57:42 +000091 Options.store(Opts, "Std", "");
Fangrui Song258a9592018-02-15 17:56:43 +000092 Options.store(Opts, "Suggest", 0);
93}
94
95void SIMDIntrinsicsCheck::registerMatchers(MatchFinder *Finder) {
96 if (!getLangOpts().CPlusPlus11)
97 return;
Fangrui Songc0e768d2018-03-07 16:57:42 +000098 // If Std is not specified, infer it from the language options.
Fangrui Song258a9592018-02-15 17:56:43 +000099 // libcxx implementation backports it to C++11 std::experimental::simd.
Fangrui Songc0e768d2018-03-07 16:57:42 +0000100 if (Std.empty())
101 Std = getLangOpts().CPlusPlus2a ? "std" : "std::experimental";
Fangrui Song258a9592018-02-15 17:56:43 +0000102
103 Finder->addMatcher(callExpr(callee(functionDecl(allOf(
104 matchesName("^::(_mm_|_mm256_|_mm512_|vec_)"),
105 isVectorFunction()))),
106 unless(isExpansionInSystemHeader()))
107 .bind("call"),
108 this);
109}
110
111void SIMDIntrinsicsCheck::check(const MatchFinder::MatchResult &Result) {
112 const auto *Call = Result.Nodes.getNodeAs<CallExpr>("call");
113 assert(Call != nullptr);
114 const FunctionDecl *Callee = Call->getDirectCallee();
115 if (!Callee)
116 return;
117
118 StringRef Old = Callee->getName();
119 StringRef New;
120 llvm::Triple::ArchType Arch =
121 Result.Context->getTargetInfo().getTriple().getArch();
122
Fangrui Songc0e768d2018-03-07 16:57:42 +0000123 // We warn or suggest if this SIMD intrinsic function has a std::simd
124 // replacement.
Fangrui Song258a9592018-02-15 17:56:43 +0000125 switch (Arch) {
Fangrui Songc0e768d2018-03-07 16:57:42 +0000126 default:
127 break;
128 case llvm::Triple::ppc:
129 case llvm::Triple::ppc64:
130 case llvm::Triple::ppc64le:
131 New = TrySuggestPPC(Old);
132 break;
133 case llvm::Triple::x86:
134 case llvm::Triple::x86_64:
135 New = TrySuggestX86(Old);
136 break;
Fangrui Song258a9592018-02-15 17:56:43 +0000137 }
138
Fangrui Songc0e768d2018-03-07 16:57:42 +0000139 // We have found a std::simd replacement.
Fangrui Song258a9592018-02-15 17:56:43 +0000140 if (!New.empty()) {
141 std::string Message;
142 // If Suggest is true, give a P0214 alternative, otherwise point it out it
143 // is non-portable.
144 if (Suggest) {
145 Message = (Twine("'") + Old + "' can be replaced by " + New).str();
146 Message = llvm::Regex("\\$std").sub(Std, Message);
Fangrui Songc0e768d2018-03-07 16:57:42 +0000147 Message =
148 llvm::Regex("\\$simd").sub((Std.str() + "::simd").str(), Message);
Fangrui Song258a9592018-02-15 17:56:43 +0000149 } else {
150 Message = (Twine("'") + Old + "' is a non-portable " +
151 llvm::Triple::getArchTypeName(Arch) + " intrinsic function")
152 .str();
153 }
154 diag(Call->getExprLoc(), Message);
155 }
156}
157
Fangrui Songc0e768d2018-03-07 16:57:42 +0000158} // namespace portability
Fangrui Song258a9592018-02-15 17:56:43 +0000159} // namespace tidy
160} // namespace clang