clang 17.0.0git
SemaRISCVVectorLookup.cpp
Go to the documentation of this file.
1//==- SemaRISCVVectorLookup.cpp - Name Lookup for RISC-V Vector Intrinsic -==//
2//
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
6//
7//===----------------------------------------------------------------------===//
8//
9// This file implements name lookup for RISC-V vector intrinsic.
10//
11//===----------------------------------------------------------------------===//
12
14#include "clang/AST/Decl.h"
18#include "clang/Sema/Lookup.h"
20#include "clang/Sema/Sema.h"
22#include "llvm/ADT/SmallVector.h"
23#include <optional>
24#include <string>
25#include <vector>
26
27using namespace llvm;
28using namespace clang;
29using namespace clang::RISCV;
30
31namespace {
32
33// Function definition of a RVV intrinsic.
34struct RVVIntrinsicDef {
35 /// Full function name with suffix, e.g. vadd_vv_i32m1.
36 std::string Name;
37
38 /// Overloaded function name, e.g. vadd.
39 std::string OverloadName;
40
41 /// Mapping to which clang built-in function, e.g. __builtin_rvv_vadd.
42 std::string BuiltinName;
43
44 /// Function signature, first element is return type.
45 RVVTypes Signature;
46};
47
48struct RVVOverloadIntrinsicDef {
49 // Indexes of RISCVIntrinsicManagerImpl::IntrinsicList.
51};
52
53} // namespace
54
56#define DECL_SIGNATURE_TABLE
57#include "clang/Basic/riscv_vector_builtin_sema.inc"
58#undef DECL_SIGNATURE_TABLE
59};
60
62#define DECL_INTRINSIC_RECORDS
63#include "clang/Basic/riscv_vector_builtin_sema.inc"
64#undef DECL_INTRINSIC_RECORDS
65};
66
67// Get subsequence of signature table.
69 uint8_t Length) {
70 return ArrayRef(&RVVSignatureTable[Index], Length);
71}
72
73static QualType RVVType2Qual(ASTContext &Context, const RVVType *Type) {
74 QualType QT;
75 switch (Type->getScalarType()) {
76 case ScalarTypeKind::Void:
77 QT = Context.VoidTy;
78 break;
79 case ScalarTypeKind::Size_t:
80 QT = Context.getSizeType();
81 break;
82 case ScalarTypeKind::Ptrdiff_t:
83 QT = Context.getPointerDiffType();
84 break;
85 case ScalarTypeKind::UnsignedLong:
86 QT = Context.UnsignedLongTy;
87 break;
88 case ScalarTypeKind::SignedLong:
89 QT = Context.LongTy;
90 break;
91 case ScalarTypeKind::Boolean:
92 QT = Context.BoolTy;
93 break;
94 case ScalarTypeKind::SignedInteger:
95 QT = Context.getIntTypeForBitwidth(Type->getElementBitwidth(), true);
96 break;
97 case ScalarTypeKind::UnsignedInteger:
98 QT = Context.getIntTypeForBitwidth(Type->getElementBitwidth(), false);
99 break;
100 case ScalarTypeKind::Float:
101 switch (Type->getElementBitwidth()) {
102 case 64:
103 QT = Context.DoubleTy;
104 break;
105 case 32:
106 QT = Context.FloatTy;
107 break;
108 case 16:
109 QT = Context.Float16Ty;
110 break;
111 default:
112 llvm_unreachable("Unsupported floating point width.");
113 }
114 break;
115 case Invalid:
116 llvm_unreachable("Unhandled type.");
117 }
118 if (Type->isVector())
119 QT = Context.getScalableVectorType(QT, *Type->getScale());
120
121 if (Type->isConstant())
122 QT = Context.getConstType(QT);
123
124 // Transform the type to a pointer as the last step, if necessary.
125 if (Type->isPointer())
126 QT = Context.getPointerType(QT);
127
128 return QT;
129}
130
131namespace {
132class RISCVIntrinsicManagerImpl : public sema::RISCVIntrinsicManager {
133private:
134 Sema &S;
135 ASTContext &Context;
136 RVVTypeCache TypeCache;
137
138 // List of all RVV intrinsic.
139 std::vector<RVVIntrinsicDef> IntrinsicList;
140 // Mapping function name to index of IntrinsicList.
141 StringMap<size_t> Intrinsics;
142 // Mapping function name to RVVOverloadIntrinsicDef.
143 StringMap<RVVOverloadIntrinsicDef> OverloadIntrinsics;
144
145 // Create IntrinsicList
146 void InitIntrinsicList();
147
148 // Create RVVIntrinsicDef.
149 void InitRVVIntrinsic(const RVVIntrinsicRecord &Record, StringRef SuffixStr,
150 StringRef OverloadedSuffixStr, bool IsMask,
151 RVVTypes &Types, bool HasPolicy, Policy PolicyAttrs);
152
153 // Create FunctionDecl for a vector intrinsic.
154 void CreateRVVIntrinsicDecl(LookupResult &LR, IdentifierInfo *II,
155 Preprocessor &PP, unsigned Index,
156 bool IsOverload);
157
158public:
159 RISCVIntrinsicManagerImpl(clang::Sema &S) : S(S), Context(S.Context) {
160 InitIntrinsicList();
161 }
162
163 // Create RISC-V vector intrinsic and insert into symbol table if found, and
164 // return true, otherwise return false.
166 Preprocessor &PP) override;
167};
168} // namespace
169
170void RISCVIntrinsicManagerImpl::InitIntrinsicList() {
171 const TargetInfo &TI = Context.getTargetInfo();
172 bool HasRV64 = TI.hasFeature("64bit");
173 bool HasFullMultiply = TI.hasFeature("v");
174
175 // Construction of RVVIntrinsicRecords need to sync with createRVVIntrinsics
176 // in RISCVVEmitter.cpp.
177 for (auto &Record : RVVIntrinsicRecords) {
178 // Create Intrinsics for each type and LMUL.
179 BasicType BaseType = BasicType::Unknown;
180 ArrayRef<PrototypeDescriptor> BasicProtoSeq =
181 ProtoSeq2ArrayRef(Record.PrototypeIndex, Record.PrototypeLength);
183 ProtoSeq2ArrayRef(Record.SuffixIndex, Record.SuffixLength);
184 ArrayRef<PrototypeDescriptor> OverloadedSuffixProto = ProtoSeq2ArrayRef(
185 Record.OverloadedSuffixIndex, Record.OverloadedSuffixSize);
186
187 PolicyScheme UnMaskedPolicyScheme =
188 static_cast<PolicyScheme>(Record.UnMaskedPolicyScheme);
189 PolicyScheme MaskedPolicyScheme =
190 static_cast<PolicyScheme>(Record.MaskedPolicyScheme);
191
192 const Policy DefaultPolicy;
193
195 RVVIntrinsic::computeBuiltinTypes(BasicProtoSeq, /*IsMasked=*/false,
196 /*HasMaskedOffOperand=*/false,
197 Record.HasVL, Record.NF,
198 UnMaskedPolicyScheme, DefaultPolicy);
199
202 BasicProtoSeq, /*IsMasked=*/true, Record.HasMaskedOffOperand,
203 Record.HasVL, Record.NF, MaskedPolicyScheme, DefaultPolicy);
204
205 bool UnMaskedHasPolicy = UnMaskedPolicyScheme != PolicyScheme::SchemeNone;
206 bool MaskedHasPolicy = MaskedPolicyScheme != PolicyScheme::SchemeNone;
207 SmallVector<Policy> SupportedUnMaskedPolicies =
209 SmallVector<Policy> SupportedMaskedPolicies =
211 Record.HasMaskPolicy);
212
213 for (unsigned int TypeRangeMaskShift = 0;
214 TypeRangeMaskShift <= static_cast<unsigned int>(BasicType::MaxOffset);
215 ++TypeRangeMaskShift) {
216 unsigned int BaseTypeI = 1 << TypeRangeMaskShift;
217 BaseType = static_cast<BasicType>(BaseTypeI);
218
219 if ((BaseTypeI & Record.TypeRangeMask) != BaseTypeI)
220 continue;
221
222 // Check requirement.
223 if (((Record.RequiredExtensions & RVV_REQ_RV64) == RVV_REQ_RV64) &&
224 !HasRV64)
225 continue;
226
227 if ((BaseType == BasicType::Int64) &&
228 ((Record.RequiredExtensions & RVV_REQ_FullMultiply) ==
230 !HasFullMultiply)
231 continue;
232
233 // Expanded with different LMUL.
234 for (int Log2LMUL = -3; Log2LMUL <= 3; Log2LMUL++) {
235 if (!(Record.Log2LMULMask & (1 << (Log2LMUL + 3))))
236 continue;
237
238 std::optional<RVVTypes> Types =
239 TypeCache.computeTypes(BaseType, Log2LMUL, Record.NF, ProtoSeq);
240
241 // Ignored to create new intrinsic if there are any illegal types.
242 if (!Types.has_value())
243 continue;
244
245 std::string SuffixStr = RVVIntrinsic::getSuffixStr(
246 TypeCache, BaseType, Log2LMUL, SuffixProto);
247 std::string OverloadedSuffixStr = RVVIntrinsic::getSuffixStr(
248 TypeCache, BaseType, Log2LMUL, OverloadedSuffixProto);
249
250 // Create non-masked intrinsic.
251 InitRVVIntrinsic(Record, SuffixStr, OverloadedSuffixStr, false, *Types,
252 UnMaskedHasPolicy, DefaultPolicy);
253
254 // Create non-masked policy intrinsic.
255 if (Record.UnMaskedPolicyScheme != PolicyScheme::SchemeNone) {
256 for (auto P : SupportedUnMaskedPolicies) {
259 BasicProtoSeq, /*IsMasked=*/false,
260 /*HasMaskedOffOperand=*/false, Record.HasVL, Record.NF,
261 UnMaskedPolicyScheme, P);
262 std::optional<RVVTypes> PolicyTypes = TypeCache.computeTypes(
263 BaseType, Log2LMUL, Record.NF, PolicyPrototype);
264 InitRVVIntrinsic(Record, SuffixStr, OverloadedSuffixStr,
265 /*IsMask=*/false, *PolicyTypes, UnMaskedHasPolicy,
266 P);
267 }
268 }
269 if (!Record.HasMasked)
270 continue;
271 // Create masked intrinsic.
272 std::optional<RVVTypes> MaskTypes =
273 TypeCache.computeTypes(BaseType, Log2LMUL, Record.NF, ProtoMaskSeq);
274 InitRVVIntrinsic(Record, SuffixStr, OverloadedSuffixStr, true,
275 *MaskTypes, MaskedHasPolicy, DefaultPolicy);
276 if (Record.MaskedPolicyScheme == PolicyScheme::SchemeNone)
277 continue;
278 // Create masked policy intrinsic.
279 for (auto P : SupportedMaskedPolicies) {
282 BasicProtoSeq, /*IsMasked=*/true, Record.HasMaskedOffOperand,
283 Record.HasVL, Record.NF, MaskedPolicyScheme, P);
284 std::optional<RVVTypes> PolicyTypes = TypeCache.computeTypes(
285 BaseType, Log2LMUL, Record.NF, PolicyPrototype);
286 InitRVVIntrinsic(Record, SuffixStr, OverloadedSuffixStr,
287 /*IsMask=*/true, *PolicyTypes, MaskedHasPolicy, P);
288 }
289 } // End for different LMUL
290 } // End for different TypeRange
291 }
292}
293
294// Compute name and signatures for intrinsic with practical types.
295void RISCVIntrinsicManagerImpl::InitRVVIntrinsic(
296 const RVVIntrinsicRecord &Record, StringRef SuffixStr,
297 StringRef OverloadedSuffixStr, bool IsMasked, RVVTypes &Signature,
298 bool HasPolicy, Policy PolicyAttrs) {
299 // Function name, e.g. vadd_vv_i32m1.
300 std::string Name = Record.Name;
301 if (!SuffixStr.empty())
302 Name += "_" + SuffixStr.str();
303
304 // Overloaded function name, e.g. vadd.
305 std::string OverloadedName;
306 if (!Record.OverloadedName)
307 OverloadedName = StringRef(Record.Name).split("_").first.str();
308 else
309 OverloadedName = Record.OverloadedName;
310 if (!OverloadedSuffixStr.empty())
311 OverloadedName += "_" + OverloadedSuffixStr.str();
312
313 // clang built-in function name, e.g. __builtin_rvv_vadd.
314 std::string BuiltinName = "__builtin_rvv_" + std::string(Record.Name);
315
316 RVVIntrinsic::updateNamesAndPolicy(IsMasked, HasPolicy, Name, BuiltinName,
317 OverloadedName, PolicyAttrs);
318
319 // Put into IntrinsicList.
320 size_t Index = IntrinsicList.size();
321 IntrinsicList.push_back({Name, OverloadedName, BuiltinName, Signature});
322
323 // Creating mapping to Intrinsics.
324 Intrinsics.insert({Name, Index});
325
326 // Get the RVVOverloadIntrinsicDef.
327 RVVOverloadIntrinsicDef &OverloadIntrinsicDef =
328 OverloadIntrinsics[OverloadedName];
329
330 // And added the index.
331 OverloadIntrinsicDef.Indexes.push_back(Index);
332}
333
334void RISCVIntrinsicManagerImpl::CreateRVVIntrinsicDecl(LookupResult &LR,
335 IdentifierInfo *II,
336 Preprocessor &PP,
337 unsigned Index,
338 bool IsOverload) {
339 ASTContext &Context = S.Context;
340 RVVIntrinsicDef &IDef = IntrinsicList[Index];
341 RVVTypes Sigs = IDef.Signature;
342 size_t SigLength = Sigs.size();
343 RVVType *ReturnType = Sigs[0];
344 QualType RetType = RVVType2Qual(Context, ReturnType);
346 QualType BuiltinFuncType;
347
348 // Skip return type, and convert RVVType to QualType for arguments.
349 for (size_t i = 1; i < SigLength; ++i)
350 ArgTypes.push_back(RVVType2Qual(Context, Sigs[i]));
351
353 Context.getDefaultCallingConvention(false, false, true));
354
355 PI.Variadic = false;
356
357 SourceLocation Loc = LR.getNameLoc();
358 BuiltinFuncType = Context.getFunctionType(RetType, ArgTypes, PI);
360
361 FunctionDecl *RVVIntrinsicDecl = FunctionDecl::Create(
362 Context, Parent, Loc, Loc, II, BuiltinFuncType, /*TInfo=*/nullptr,
364 /*isInlineSpecified*/ false,
365 /*hasWrittenPrototype*/ true);
366
367 // Create Decl objects for each parameter, adding them to the
368 // FunctionDecl.
369 const auto *FP = cast<FunctionProtoType>(BuiltinFuncType);
371 for (unsigned IParm = 0, E = FP->getNumParams(); IParm != E; ++IParm) {
372 ParmVarDecl *Parm =
373 ParmVarDecl::Create(Context, RVVIntrinsicDecl, Loc, Loc, nullptr,
374 FP->getParamType(IParm), nullptr, SC_None, nullptr);
375 Parm->setScopeInfo(0, IParm);
376 ParmList.push_back(Parm);
377 }
378 RVVIntrinsicDecl->setParams(ParmList);
379
380 // Add function attributes.
381 if (IsOverload)
382 RVVIntrinsicDecl->addAttr(OverloadableAttr::CreateImplicit(Context));
383
384 // Setup alias to __builtin_rvv_*
385 IdentifierInfo &IntrinsicII = PP.getIdentifierTable().get(IDef.BuiltinName);
386 RVVIntrinsicDecl->addAttr(
387 BuiltinAliasAttr::CreateImplicit(S.Context, &IntrinsicII));
388
389 // Add to symbol table.
390 LR.addDecl(RVVIntrinsicDecl);
391}
392
393bool RISCVIntrinsicManagerImpl::CreateIntrinsicIfFound(LookupResult &LR,
394 IdentifierInfo *II,
395 Preprocessor &PP) {
396 StringRef Name = II->getName();
397
398 // Lookup the function name from the overload intrinsics first.
399 auto OvIItr = OverloadIntrinsics.find(Name);
400 if (OvIItr != OverloadIntrinsics.end()) {
401 const RVVOverloadIntrinsicDef &OvIntrinsicDef = OvIItr->second;
402 for (auto Index : OvIntrinsicDef.Indexes)
403 CreateRVVIntrinsicDecl(LR, II, PP, Index,
404 /*IsOverload*/ true);
405
406 // If we added overloads, need to resolve the lookup result.
407 LR.resolveKind();
408 return true;
409 }
410
411 // Lookup the function name from the intrinsics.
412 auto Itr = Intrinsics.find(Name);
413 if (Itr != Intrinsics.end()) {
414 CreateRVVIntrinsicDecl(LR, II, PP, Itr->second,
415 /*IsOverload*/ false);
416 return true;
417 }
418
419 // It's not an RVV intrinsics.
420 return false;
421}
422
423namespace clang {
424std::unique_ptr<clang::sema::RISCVIntrinsicManager>
426 return std::make_unique<RISCVIntrinsicManagerImpl>(S);
427}
428} // namespace clang
Defines the clang::ASTContext interface.
NodeId Parent
Definition: ASTDiff.cpp:191
StringRef P
Defines enum values for all the target-independent builtin functions.
Defines the clang::Preprocessor interface.
static ArrayRef< PrototypeDescriptor > ProtoSeq2ArrayRef(uint16_t Index, uint8_t Length)
static const RVVIntrinsicRecord RVVIntrinsicRecords[]
static QualType RVVType2Qual(ASTContext &Context, const RVVType *Type)
static const PrototypeDescriptor RVVSignatureTable[]
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition: ASTContext.h:182
TranslationUnitDecl * getTranslationUnitDecl() const
Definition: ASTContext.h:1060
CanQualType LongTy
Definition: ASTContext.h:1087
CanQualType FloatTy
Definition: ASTContext.h:1090
CanQualType DoubleTy
Definition: ASTContext.h:1090
CallingConv getDefaultCallingConvention(bool IsVariadic, bool IsCXXMethod, bool IsBuiltin=false) const
Retrieves the default calling convention for the current target.
QualType getPointerType(QualType T) const
Return the uniqued reference to the type for a pointer to the specified type.
QualType getConstType(QualType T) const
Return the uniqued reference to the type for a const qualified type.
Definition: ASTContext.h:1276
QualType getPointerDiffType() const
Return the unique type for "ptrdiff_t" (C99 7.17) defined in <stddef.h>.
CanQualType BoolTy
Definition: ASTContext.h:1079
QualType getIntTypeForBitwidth(unsigned DestWidth, unsigned Signed) const
getIntTypeForBitwidth - sets integer QualTy according to specified details: bitwidth,...
CanQualType UnsignedLongTy
Definition: ASTContext.h:1088
CanQualType getSizeType() const
Return the unique type for "size_t" (C99 7.17), defined in <stddef.h>.
CanQualType Float16Ty
Definition: ASTContext.h:1104
CanQualType VoidTy
Definition: ASTContext.h:1078
QualType getFunctionType(QualType ResultTy, ArrayRef< QualType > Args, const FunctionProtoType::ExtProtoInfo &EPI) const
Return a normal function type with a typed argument list.
Definition: ASTContext.h:1537
const TargetInfo & getTargetInfo() const
Definition: ASTContext.h:744
QualType getScalableVectorType(QualType EltTy, unsigned NumElts) const
Return the unique reference to a scalable vector type of the specified element type and scalable numb...
DeclContext - This is used only as base class of specific decl types that can act as declaration cont...
Definition: DeclBase.h:1393
void addAttr(Attr *A)
Definition: DeclBase.cpp:903
bool isFPConstrained() const
Definition: LangOptions.h:736
Represents a function declaration or definition.
Definition: Decl.h:1917
static FunctionDecl * Create(ASTContext &C, DeclContext *DC, SourceLocation StartLoc, SourceLocation NLoc, DeclarationName N, QualType T, TypeSourceInfo *TInfo, StorageClass SC, bool UsesFPIntrin=false, bool isInlineSpecified=false, bool hasWrittenPrototype=true, ConstexprSpecKind ConstexprKind=ConstexprSpecKind::Unspecified, Expr *TrailingRequiresClause=nullptr)
Definition: Decl.h:2093
One of these records is kept for each identifier that is lexed.
StringRef getName() const
Return the actual identifier string.
IdentifierInfo & get(StringRef Name)
Return the identifier token info for the specified named identifier.
Represents the results of name lookup.
Definition: Lookup.h:46
void addDecl(NamedDecl *D)
Add a declaration to these results with its natural access.
Definition: Lookup.h:452
void resolveKind()
Resolves the result kind of the lookup, possibly hiding decls.
Definition: SemaLookup.cpp:482
SourceLocation getNameLoc() const
Gets the location of the identifier.
Definition: Lookup.h:632
Represents a parameter to a function.
Definition: Decl.h:1722
void setScopeInfo(unsigned scopeDepth, unsigned parameterIndex)
Definition: Decl.h:1755
static ParmVarDecl * Create(ASTContext &C, DeclContext *DC, SourceLocation StartLoc, SourceLocation IdLoc, IdentifierInfo *Id, QualType T, TypeSourceInfo *TInfo, StorageClass S, Expr *DefArg)
Definition: Decl.cpp:2851
Engages in a tight little dance with the lexer to efficiently preprocess tokens.
Definition: Preprocessor.h:128
IdentifierTable & getIdentifierTable()
A (possibly-)qualified type.
Definition: Type.h:736
static llvm::SmallVector< Policy > getSupportedMaskedPolicies(bool HasTailPolicy, bool HasMaskPolicy)
static std::string getSuffixStr(RVVTypeCache &TypeCache, BasicType Type, int Log2LMUL, llvm::ArrayRef< PrototypeDescriptor > PrototypeDescriptors)
static void updateNamesAndPolicy(bool IsMasked, bool HasPolicy, std::string &Name, std::string &BuiltinName, std::string &OverloadedName, Policy &PolicyAttrs)
static llvm::SmallVector< PrototypeDescriptor > computeBuiltinTypes(llvm::ArrayRef< PrototypeDescriptor > Prototype, bool IsMasked, bool HasMaskedOffOperand, bool HasVL, unsigned NF, PolicyScheme DefaultScheme, Policy PolicyAttrs)
static llvm::SmallVector< Policy > getSupportedUnMaskedPolicies()
Sema - This implements semantic analysis and AST building for C.
Definition: Sema.h:356
ASTContext & Context
Definition: Sema.h:407
FPOptions & getCurFPFeatures()
Definition: Sema.h:1647
Encodes a location in the source.
Exposes information about the current target.
Definition: TargetInfo.h:206
virtual bool hasFeature(StringRef Feature) const
Determine whether the given target has the given feature.
Definition: TargetInfo.h:1386
The base class of the type hierarchy.
Definition: Type.h:1566
virtual bool CreateIntrinsicIfFound(LookupResult &LR, IdentifierInfo *II, Preprocessor &PP)=0
Defines the clang::TargetInfo interface.
RISCV builtins.
std::vector< RVVTypePtr > RVVTypes
std::unique_ptr< sema::RISCVIntrinsicManager > CreateRISCVIntrinsicManager(Sema &S)
@ SC_Extern
Definition: Specifiers.h:239
@ SC_None
Definition: Specifiers.h:238
YAML serialization mapping.
Definition: Dominators.h:30
Extra information about a function prototype.
Definition: Type.h:4118