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
13#include "clang/AST/ASTContext.h"
14#include "clang/AST/Decl.h"
15#include "clang/Basic/Builtins.h"
16#include "clang/Basic/TargetInfo.h"
17#include "clang/Lex/Preprocessor.h"
18#include "clang/Sema/Lookup.h"
19#include "clang/Sema/RISCVIntrinsicManager.h"
20#include "clang/Sema/Sema.h"
21#include "clang/Support/RISCVVIntrinsicUtils.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
31using IntrinsicKind = sema::RISCVIntrinsicManager::IntrinsicKind;
32
33namespace {
34
35// Function definition of a RVV intrinsic.
36struct RVVIntrinsicDef {
37 /// Mapping to which clang built-in function, e.g. __builtin_rvv_vadd.
38 std::string BuiltinName;
39
40 /// Function signature, first element is return type.
41 RVVTypes Signature;
42};
43
44struct RVVOverloadIntrinsicDef {
45 // Indexes of RISCVIntrinsicManagerImpl::IntrinsicList.
46 SmallVector<uint16_t, 8> Indexes;
47};
48
49} // namespace
50
51static const PrototypeDescriptor RVVSignatureTable[] = {
52#define DECL_SIGNATURE_TABLE
53#include "clang/Basic/riscv_vector_builtin_sema.inc"
54#undef DECL_SIGNATURE_TABLE
55};
56
57static const PrototypeDescriptor RVSiFiveVectorSignatureTable[] = {
58#define DECL_SIGNATURE_TABLE
59#include "clang/Basic/riscv_sifive_vector_builtin_sema.inc"
60#undef DECL_SIGNATURE_TABLE
61};
62
63static const RVVIntrinsicRecord RVVIntrinsicRecords[] = {
64#define DECL_INTRINSIC_RECORDS
65#include "clang/Basic/riscv_vector_builtin_sema.inc"
66#undef DECL_INTRINSIC_RECORDS
67};
68
69static const RVVIntrinsicRecord RVSiFiveVectorIntrinsicRecords[] = {
70#define DECL_INTRINSIC_RECORDS
71#include "clang/Basic/riscv_sifive_vector_builtin_sema.inc"
72#undef DECL_INTRINSIC_RECORDS
73};
74
75// Get subsequence of signature table.
76static ArrayRef<PrototypeDescriptor>
77ProtoSeq2ArrayRef(IntrinsicKind K, uint16_t Index, uint8_t Length) {
78 switch (K) {
79 case IntrinsicKind::RVV:
80 return ArrayRef(&RVVSignatureTable[Index], Length);
81 case IntrinsicKind::SIFIVE_VECTOR:
82 return ArrayRef(&RVSiFiveVectorSignatureTable[Index], Length);
83 }
84 llvm_unreachable("Unhandled IntrinsicKind");
85}
86
87static QualType RVVType2Qual(ASTContext &Context, const RVVType *Type) {
88 QualType QT;
89 switch (Type->getScalarType()) {
90 case ScalarTypeKind::Void:
91 QT = Context.VoidTy;
92 break;
93 case ScalarTypeKind::Size_t:
94 QT = Context.getSizeType();
95 break;
96 case ScalarTypeKind::Ptrdiff_t:
97 QT = Context.getPointerDiffType();
98 break;
99 case ScalarTypeKind::UnsignedLong:
100 QT = Context.UnsignedLongTy;
101 break;
102 case ScalarTypeKind::SignedLong:
103 QT = Context.LongTy;
104 break;
105 case ScalarTypeKind::Boolean:
106 QT = Context.BoolTy;
107 break;
108 case ScalarTypeKind::SignedInteger:
109 QT = Context.getIntTypeForBitwidth(DestWidth: Type->getElementBitwidth(), Signed: true);
110 break;
111 case ScalarTypeKind::UnsignedInteger:
112 QT = Context.getIntTypeForBitwidth(DestWidth: Type->getElementBitwidth(), Signed: false);
113 break;
114 case ScalarTypeKind::BFloat:
115 QT = Context.BFloat16Ty;
116 break;
117 case ScalarTypeKind::Float:
118 switch (Type->getElementBitwidth()) {
119 case 64:
120 QT = Context.DoubleTy;
121 break;
122 case 32:
123 QT = Context.FloatTy;
124 break;
125 case 16:
126 QT = Context.Float16Ty;
127 break;
128 default:
129 llvm_unreachable("Unsupported floating point width.");
130 }
131 break;
132 case Invalid:
133 case Undefined:
134 llvm_unreachable("Unhandled type.");
135 }
136 if (Type->isVector()) {
137 if (Type->isTuple())
138 QT = Context.getScalableVectorType(EltTy: QT, NumElts: *Type->getScale(), NumFields: Type->getNF());
139 else
140 QT = Context.getScalableVectorType(EltTy: QT, NumElts: *Type->getScale());
141 }
142
143 if (Type->isConstant())
144 QT = Context.getConstType(T: QT);
145
146 // Transform the type to a pointer as the last step, if necessary.
147 if (Type->isPointer())
148 QT = Context.getPointerType(T: QT);
149
150 return QT;
151}
152
153namespace {
154class RISCVIntrinsicManagerImpl : public sema::RISCVIntrinsicManager {
155private:
156 Sema &S;
157 ASTContext &Context;
158 RVVTypeCache TypeCache;
159 bool ConstructedRISCVVBuiltins;
160 bool ConstructedRISCVSiFiveVectorBuiltins;
161
162 // List of all RVV intrinsic.
163 std::vector<RVVIntrinsicDef> IntrinsicList;
164 // Mapping function name to index of IntrinsicList.
165 StringMap<uint16_t> Intrinsics;
166 // Mapping function name to RVVOverloadIntrinsicDef.
167 StringMap<RVVOverloadIntrinsicDef> OverloadIntrinsics;
168
169
170 // Create RVVIntrinsicDef.
171 void InitRVVIntrinsic(const RVVIntrinsicRecord &Record, StringRef SuffixStr,
172 StringRef OverloadedSuffixStr, bool IsMask,
173 RVVTypes &Types, bool HasPolicy, Policy PolicyAttrs);
174
175 // Create FunctionDecl for a vector intrinsic.
176 void CreateRVVIntrinsicDecl(LookupResult &LR, IdentifierInfo *II,
177 Preprocessor &PP, uint32_t Index,
178 bool IsOverload);
179
180 void ConstructRVVIntrinsics(ArrayRef<RVVIntrinsicRecord> Recs,
181 IntrinsicKind K);
182
183public:
184 RISCVIntrinsicManagerImpl(clang::Sema &S) : S(S), Context(S.Context) {
185 ConstructedRISCVVBuiltins = false;
186 ConstructedRISCVSiFiveVectorBuiltins = false;
187 }
188
189 // Initialize IntrinsicList
190 void InitIntrinsicList() override;
191
192 // Create RISC-V vector intrinsic and insert into symbol table if found, and
193 // return true, otherwise return false.
194 bool CreateIntrinsicIfFound(LookupResult &LR, IdentifierInfo *II,
195 Preprocessor &PP) override;
196};
197} // namespace
198
199void RISCVIntrinsicManagerImpl::ConstructRVVIntrinsics(
200 ArrayRef<RVVIntrinsicRecord> Recs, IntrinsicKind K) {
201 const TargetInfo &TI = Context.getTargetInfo();
202 static const std::pair<const char *, RVVRequire> FeatureCheckList[] = {
203 {"64bit", RVV_REQ_RV64},
204 {"xsfvcp", RVV_REQ_Xsfvcp},
205 {"xsfvfnrclipxfqf", RVV_REQ_Xsfvfnrclipxfqf},
206 {"xsfvfwmaccqqq", RVV_REQ_Xsfvfwmaccqqq},
207 {"xsfvqmaccdod", RVV_REQ_Xsfvqmaccdod},
208 {"xsfvqmaccqoq", RVV_REQ_Xsfvqmaccqoq},
209 {"zvbb", RVV_REQ_Zvbb},
210 {"zvbc", RVV_REQ_Zvbc},
211 {"zvkb", RVV_REQ_Zvkb},
212 {"zvkg", RVV_REQ_Zvkg},
213 {"zvkned", RVV_REQ_Zvkned},
214 {"zvknha", RVV_REQ_Zvknha},
215 {"zvknhb", RVV_REQ_Zvknhb},
216 {"zvksed", RVV_REQ_Zvksed},
217 {"zvksh", RVV_REQ_Zvksh},
218 {"zvfbfwma", RVV_REQ_Zvfbfwma},
219 {"experimental", RVV_REQ_Experimental}};
220
221 // Construction of RVVIntrinsicRecords need to sync with createRVVIntrinsics
222 // in RISCVVEmitter.cpp.
223 for (auto &Record : Recs) {
224 // Check requirements.
225 if (llvm::any_of(Range: FeatureCheckList, P: [&](const auto &Item) {
226 return (Record.RequiredExtensions & Item.second) == Item.second &&
227 !TI.hasFeature(Feature: Item.first);
228 }))
229 continue;
230
231 // Create Intrinsics for each type and LMUL.
232 BasicType BaseType = BasicType::Unknown;
233 ArrayRef<PrototypeDescriptor> BasicProtoSeq =
234 ProtoSeq2ArrayRef(K, Index: Record.PrototypeIndex, Length: Record.PrototypeLength);
235 ArrayRef<PrototypeDescriptor> SuffixProto =
236 ProtoSeq2ArrayRef(K, Index: Record.SuffixIndex, Length: Record.SuffixLength);
237 ArrayRef<PrototypeDescriptor> OverloadedSuffixProto = ProtoSeq2ArrayRef(
238 K, Index: Record.OverloadedSuffixIndex, Length: Record.OverloadedSuffixSize);
239
240 PolicyScheme UnMaskedPolicyScheme =
241 static_cast<PolicyScheme>(Record.UnMaskedPolicyScheme);
242 PolicyScheme MaskedPolicyScheme =
243 static_cast<PolicyScheme>(Record.MaskedPolicyScheme);
244
245 const Policy DefaultPolicy;
246
247 llvm::SmallVector<PrototypeDescriptor> ProtoSeq =
248 RVVIntrinsic::computeBuiltinTypes(
249 Prototype: BasicProtoSeq, /*IsMasked=*/false,
250 /*HasMaskedOffOperand=*/false, HasVL: Record.HasVL, NF: Record.NF,
251 DefaultScheme: UnMaskedPolicyScheme, PolicyAttrs: DefaultPolicy, IsTuple: Record.IsTuple);
252
253 llvm::SmallVector<PrototypeDescriptor> ProtoMaskSeq;
254 if (Record.HasMasked)
255 ProtoMaskSeq = RVVIntrinsic::computeBuiltinTypes(
256 Prototype: BasicProtoSeq, /*IsMasked=*/true, HasMaskedOffOperand: Record.HasMaskedOffOperand,
257 HasVL: Record.HasVL, NF: Record.NF, DefaultScheme: MaskedPolicyScheme, PolicyAttrs: DefaultPolicy,
258 IsTuple: Record.IsTuple);
259
260 bool UnMaskedHasPolicy = UnMaskedPolicyScheme != PolicyScheme::SchemeNone;
261 bool MaskedHasPolicy = MaskedPolicyScheme != PolicyScheme::SchemeNone;
262 SmallVector<Policy> SupportedUnMaskedPolicies =
263 RVVIntrinsic::getSupportedUnMaskedPolicies();
264 SmallVector<Policy> SupportedMaskedPolicies =
265 RVVIntrinsic::getSupportedMaskedPolicies(HasTailPolicy: Record.HasTailPolicy,
266 HasMaskPolicy: Record.HasMaskPolicy);
267
268 for (unsigned int TypeRangeMaskShift = 0;
269 TypeRangeMaskShift <= static_cast<unsigned int>(BasicType::MaxOffset);
270 ++TypeRangeMaskShift) {
271 unsigned int BaseTypeI = 1 << TypeRangeMaskShift;
272 BaseType = static_cast<BasicType>(BaseTypeI);
273
274 if ((BaseTypeI & Record.TypeRangeMask) != BaseTypeI)
275 continue;
276
277 if (BaseType == BasicType::Float16) {
278 if ((Record.RequiredExtensions & RVV_REQ_Zvfhmin) == RVV_REQ_Zvfhmin) {
279 if (!TI.hasFeature(Feature: "zvfhmin"))
280 continue;
281 } else if (!TI.hasFeature(Feature: "zvfh")) {
282 continue;
283 }
284 }
285
286 // Expanded with different LMUL.
287 for (int Log2LMUL = -3; Log2LMUL <= 3; Log2LMUL++) {
288 if (!(Record.Log2LMULMask & (1 << (Log2LMUL + 3))))
289 continue;
290
291 std::optional<RVVTypes> Types =
292 TypeCache.computeTypes(BT: BaseType, Log2LMUL, NF: Record.NF, Prototype: ProtoSeq);
293
294 // Ignored to create new intrinsic if there are any illegal types.
295 if (!Types.has_value())
296 continue;
297
298 std::string SuffixStr = RVVIntrinsic::getSuffixStr(
299 TypeCache, Type: BaseType, Log2LMUL, PrototypeDescriptors: SuffixProto);
300 std::string OverloadedSuffixStr = RVVIntrinsic::getSuffixStr(
301 TypeCache, Type: BaseType, Log2LMUL, PrototypeDescriptors: OverloadedSuffixProto);
302
303 // Create non-masked intrinsic.
304 InitRVVIntrinsic(Record, SuffixStr, OverloadedSuffixStr, IsMask: false, Types&: *Types,
305 HasPolicy: UnMaskedHasPolicy, PolicyAttrs: DefaultPolicy);
306
307 // Create non-masked policy intrinsic.
308 if (Record.UnMaskedPolicyScheme != PolicyScheme::SchemeNone) {
309 for (auto P : SupportedUnMaskedPolicies) {
310 llvm::SmallVector<PrototypeDescriptor> PolicyPrototype =
311 RVVIntrinsic::computeBuiltinTypes(
312 Prototype: BasicProtoSeq, /*IsMasked=*/false,
313 /*HasMaskedOffOperand=*/false, HasVL: Record.HasVL, NF: Record.NF,
314 DefaultScheme: UnMaskedPolicyScheme, PolicyAttrs: P, IsTuple: Record.IsTuple);
315 std::optional<RVVTypes> PolicyTypes = TypeCache.computeTypes(
316 BT: BaseType, Log2LMUL, NF: Record.NF, Prototype: PolicyPrototype);
317 InitRVVIntrinsic(Record, SuffixStr, OverloadedSuffixStr,
318 /*IsMask=*/false, Types&: *PolicyTypes, HasPolicy: UnMaskedHasPolicy,
319 PolicyAttrs: P);
320 }
321 }
322 if (!Record.HasMasked)
323 continue;
324 // Create masked intrinsic.
325 std::optional<RVVTypes> MaskTypes =
326 TypeCache.computeTypes(BT: BaseType, Log2LMUL, NF: Record.NF, Prototype: ProtoMaskSeq);
327 InitRVVIntrinsic(Record, SuffixStr, OverloadedSuffixStr, IsMask: true,
328 Types&: *MaskTypes, HasPolicy: MaskedHasPolicy, PolicyAttrs: DefaultPolicy);
329 if (Record.MaskedPolicyScheme == PolicyScheme::SchemeNone)
330 continue;
331 // Create masked policy intrinsic.
332 for (auto P : SupportedMaskedPolicies) {
333 llvm::SmallVector<PrototypeDescriptor> PolicyPrototype =
334 RVVIntrinsic::computeBuiltinTypes(
335 Prototype: BasicProtoSeq, /*IsMasked=*/true, HasMaskedOffOperand: Record.HasMaskedOffOperand,
336 HasVL: Record.HasVL, NF: Record.NF, DefaultScheme: MaskedPolicyScheme, PolicyAttrs: P,
337 IsTuple: Record.IsTuple);
338 std::optional<RVVTypes> PolicyTypes = TypeCache.computeTypes(
339 BT: BaseType, Log2LMUL, NF: Record.NF, Prototype: PolicyPrototype);
340 InitRVVIntrinsic(Record, SuffixStr, OverloadedSuffixStr,
341 /*IsMask=*/true, Types&: *PolicyTypes, HasPolicy: MaskedHasPolicy, PolicyAttrs: P);
342 }
343 } // End for different LMUL
344 } // End for different TypeRange
345 }
346}
347
348void RISCVIntrinsicManagerImpl::InitIntrinsicList() {
349
350 if (S.DeclareRISCVVBuiltins && !ConstructedRISCVVBuiltins) {
351 ConstructedRISCVVBuiltins = true;
352 ConstructRVVIntrinsics(RVVIntrinsicRecords,
353 IntrinsicKind::RVV);
354 }
355 if (S.DeclareRISCVSiFiveVectorBuiltins &&
356 !ConstructedRISCVSiFiveVectorBuiltins) {
357 ConstructedRISCVSiFiveVectorBuiltins = true;
358 ConstructRVVIntrinsics(RVSiFiveVectorIntrinsicRecords,
359 IntrinsicKind::SIFIVE_VECTOR);
360 }
361}
362
363// Compute name and signatures for intrinsic with practical types.
364void RISCVIntrinsicManagerImpl::InitRVVIntrinsic(
365 const RVVIntrinsicRecord &Record, StringRef SuffixStr,
366 StringRef OverloadedSuffixStr, bool IsMasked, RVVTypes &Signature,
367 bool HasPolicy, Policy PolicyAttrs) {
368 // Function name, e.g. vadd_vv_i32m1.
369 std::string Name = Record.Name;
370 if (!SuffixStr.empty())
371 Name += "_" + SuffixStr.str();
372
373 // Overloaded function name, e.g. vadd.
374 std::string OverloadedName;
375 if (!Record.OverloadedName)
376 OverloadedName = StringRef(Record.Name).split(Separator: "_").first.str();
377 else
378 OverloadedName = Record.OverloadedName;
379 if (!OverloadedSuffixStr.empty())
380 OverloadedName += "_" + OverloadedSuffixStr.str();
381
382 // clang built-in function name, e.g. __builtin_rvv_vadd.
383 std::string BuiltinName = std::string(Record.Name);
384
385 RVVIntrinsic::updateNamesAndPolicy(IsMasked, HasPolicy, Name, BuiltinName,
386 OverloadedName, PolicyAttrs,
387 HasFRMRoundModeOp: Record.HasFRMRoundModeOp);
388
389 // Put into IntrinsicList.
390 uint16_t Index = IntrinsicList.size();
391 assert(IntrinsicList.size() == (size_t)Index &&
392 "Intrinsics indices overflow.");
393 IntrinsicList.push_back(x: {.BuiltinName: BuiltinName, .Signature: Signature});
394
395 // Creating mapping to Intrinsics.
396 Intrinsics.insert(KV: {Name, Index});
397
398 // Get the RVVOverloadIntrinsicDef.
399 RVVOverloadIntrinsicDef &OverloadIntrinsicDef =
400 OverloadIntrinsics[OverloadedName];
401
402 // And added the index.
403 OverloadIntrinsicDef.Indexes.push_back(Elt: Index);
404}
405
406void RISCVIntrinsicManagerImpl::CreateRVVIntrinsicDecl(LookupResult &LR,
407 IdentifierInfo *II,
408 Preprocessor &PP,
409 uint32_t Index,
410 bool IsOverload) {
411 ASTContext &Context = S.Context;
412 RVVIntrinsicDef &IDef = IntrinsicList[Index];
413 RVVTypes Sigs = IDef.Signature;
414 size_t SigLength = Sigs.size();
415 RVVType *ReturnType = Sigs[0];
416 QualType RetType = RVVType2Qual(Context, Type: ReturnType);
417 SmallVector<QualType, 8> ArgTypes;
418 QualType BuiltinFuncType;
419
420 // Skip return type, and convert RVVType to QualType for arguments.
421 for (size_t i = 1; i < SigLength; ++i)
422 ArgTypes.push_back(Elt: RVVType2Qual(Context, Type: Sigs[i]));
423
424 FunctionProtoType::ExtProtoInfo PI(
425 Context.getDefaultCallingConvention(IsVariadic: false, IsCXXMethod: false, IsBuiltin: true));
426
427 PI.Variadic = false;
428
429 SourceLocation Loc = LR.getNameLoc();
430 BuiltinFuncType = Context.getFunctionType(ResultTy: RetType, Args: ArgTypes, EPI: PI);
431 DeclContext *Parent = Context.getTranslationUnitDecl();
432
433 FunctionDecl *RVVIntrinsicDecl = FunctionDecl::Create(
434 C&: Context, DC: Parent, StartLoc: Loc, NLoc: Loc, N: II, T: BuiltinFuncType, /*TInfo=*/nullptr,
435 SC: SC_Extern, UsesFPIntrin: S.getCurFPFeatures().isFPConstrained(),
436 /*isInlineSpecified*/ false,
437 /*hasWrittenPrototype*/ true);
438
439 // Create Decl objects for each parameter, adding them to the
440 // FunctionDecl.
441 const auto *FP = cast<FunctionProtoType>(Val&: BuiltinFuncType);
442 SmallVector<ParmVarDecl *, 8> ParmList;
443 for (unsigned IParm = 0, E = FP->getNumParams(); IParm != E; ++IParm) {
444 ParmVarDecl *Parm =
445 ParmVarDecl::Create(Context, RVVIntrinsicDecl, Loc, Loc, nullptr,
446 FP->getParamType(i: IParm), nullptr, SC_None, nullptr);
447 Parm->setScopeInfo(scopeDepth: 0, parameterIndex: IParm);
448 ParmList.push_back(Elt: Parm);
449 }
450 RVVIntrinsicDecl->setParams(ParmList);
451
452 // Add function attributes.
453 if (IsOverload)
454 RVVIntrinsicDecl->addAttr(OverloadableAttr::CreateImplicit(Context));
455
456 // Setup alias to __builtin_rvv_*
457 IdentifierInfo &IntrinsicII =
458 PP.getIdentifierTable().get(Name: "__builtin_rvv_" + IDef.BuiltinName);
459 RVVIntrinsicDecl->addAttr(
460 BuiltinAliasAttr::CreateImplicit(S.Context, &IntrinsicII));
461
462 // Add to symbol table.
463 LR.addDecl(RVVIntrinsicDecl);
464}
465
466bool RISCVIntrinsicManagerImpl::CreateIntrinsicIfFound(LookupResult &LR,
467 IdentifierInfo *II,
468 Preprocessor &PP) {
469 StringRef Name = II->getName();
470 if (!Name.consume_front(Prefix: "__riscv_"))
471 return false;
472
473 // Lookup the function name from the overload intrinsics first.
474 auto OvIItr = OverloadIntrinsics.find(Key: Name);
475 if (OvIItr != OverloadIntrinsics.end()) {
476 const RVVOverloadIntrinsicDef &OvIntrinsicDef = OvIItr->second;
477 for (auto Index : OvIntrinsicDef.Indexes)
478 CreateRVVIntrinsicDecl(LR, II, PP, Index,
479 /*IsOverload*/ true);
480
481 // If we added overloads, need to resolve the lookup result.
482 LR.resolveKind();
483 return true;
484 }
485
486 // Lookup the function name from the intrinsics.
487 auto Itr = Intrinsics.find(Key: Name);
488 if (Itr != Intrinsics.end()) {
489 CreateRVVIntrinsicDecl(LR, II, PP, Index: Itr->second,
490 /*IsOverload*/ false);
491 return true;
492 }
493
494 // It's not an RVV intrinsics.
495 return false;
496}
497
498namespace clang {
499std::unique_ptr<clang::sema::RISCVIntrinsicManager>
500CreateRISCVIntrinsicManager(Sema &S) {
501 return std::make_unique<RISCVIntrinsicManagerImpl>(args&: S);
502}
503} // namespace clang
504

source code of clang/lib/Sema/SemaRISCVVectorLookup.cpp