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 {"zvfbfmin", RVV_REQ_Zvfbfmin},
220 {"experimental", RVV_REQ_Experimental}};
221
222 // Construction of RVVIntrinsicRecords need to sync with createRVVIntrinsics
223 // in RISCVVEmitter.cpp.
224 for (auto &Record : Recs) {
225 // Check requirements.
226 if (llvm::any_of(Range: FeatureCheckList, P: [&](const auto &Item) {
227 return (Record.RequiredExtensions & Item.second) == Item.second &&
228 !TI.hasFeature(Feature: Item.first);
229 }))
230 continue;
231
232 // Create Intrinsics for each type and LMUL.
233 BasicType BaseType = BasicType::Unknown;
234 ArrayRef<PrototypeDescriptor> BasicProtoSeq =
235 ProtoSeq2ArrayRef(K, Index: Record.PrototypeIndex, Length: Record.PrototypeLength);
236 ArrayRef<PrototypeDescriptor> SuffixProto =
237 ProtoSeq2ArrayRef(K, Index: Record.SuffixIndex, Length: Record.SuffixLength);
238 ArrayRef<PrototypeDescriptor> OverloadedSuffixProto = ProtoSeq2ArrayRef(
239 K, Index: Record.OverloadedSuffixIndex, Length: Record.OverloadedSuffixSize);
240
241 PolicyScheme UnMaskedPolicyScheme =
242 static_cast<PolicyScheme>(Record.UnMaskedPolicyScheme);
243 PolicyScheme MaskedPolicyScheme =
244 static_cast<PolicyScheme>(Record.MaskedPolicyScheme);
245
246 const Policy DefaultPolicy;
247
248 llvm::SmallVector<PrototypeDescriptor> ProtoSeq =
249 RVVIntrinsic::computeBuiltinTypes(
250 Prototype: BasicProtoSeq, /*IsMasked=*/false,
251 /*HasMaskedOffOperand=*/false, HasVL: Record.HasVL, NF: Record.NF,
252 DefaultScheme: UnMaskedPolicyScheme, PolicyAttrs: DefaultPolicy, IsTuple: Record.IsTuple);
253
254 llvm::SmallVector<PrototypeDescriptor> ProtoMaskSeq;
255 if (Record.HasMasked)
256 ProtoMaskSeq = RVVIntrinsic::computeBuiltinTypes(
257 Prototype: BasicProtoSeq, /*IsMasked=*/true, HasMaskedOffOperand: Record.HasMaskedOffOperand,
258 HasVL: Record.HasVL, NF: Record.NF, DefaultScheme: MaskedPolicyScheme, PolicyAttrs: DefaultPolicy,
259 IsTuple: Record.IsTuple);
260
261 bool UnMaskedHasPolicy = UnMaskedPolicyScheme != PolicyScheme::SchemeNone;
262 bool MaskedHasPolicy = MaskedPolicyScheme != PolicyScheme::SchemeNone;
263 SmallVector<Policy> SupportedUnMaskedPolicies =
264 RVVIntrinsic::getSupportedUnMaskedPolicies();
265 SmallVector<Policy> SupportedMaskedPolicies =
266 RVVIntrinsic::getSupportedMaskedPolicies(HasTailPolicy: Record.HasTailPolicy,
267 HasMaskPolicy: Record.HasMaskPolicy);
268
269 for (unsigned int TypeRangeMaskShift = 0;
270 TypeRangeMaskShift <= static_cast<unsigned int>(BasicType::MaxOffset);
271 ++TypeRangeMaskShift) {
272 unsigned int BaseTypeI = 1 << TypeRangeMaskShift;
273 BaseType = static_cast<BasicType>(BaseTypeI);
274
275 if ((BaseTypeI & Record.TypeRangeMask) != BaseTypeI)
276 continue;
277
278 if (BaseType == BasicType::Float16) {
279 if ((Record.RequiredExtensions & RVV_REQ_Zvfhmin) == RVV_REQ_Zvfhmin) {
280 if (!TI.hasFeature(Feature: "zvfhmin"))
281 continue;
282 } else if (!TI.hasFeature(Feature: "zvfh")) {
283 continue;
284 }
285 }
286
287 // Expanded with different LMUL.
288 for (int Log2LMUL = -3; Log2LMUL <= 3; Log2LMUL++) {
289 if (!(Record.Log2LMULMask & (1 << (Log2LMUL + 3))))
290 continue;
291
292 std::optional<RVVTypes> Types =
293 TypeCache.computeTypes(BT: BaseType, Log2LMUL, NF: Record.NF, Prototype: ProtoSeq);
294
295 // Ignored to create new intrinsic if there are any illegal types.
296 if (!Types.has_value())
297 continue;
298
299 std::string SuffixStr = RVVIntrinsic::getSuffixStr(
300 TypeCache, Type: BaseType, Log2LMUL, PrototypeDescriptors: SuffixProto);
301 std::string OverloadedSuffixStr = RVVIntrinsic::getSuffixStr(
302 TypeCache, Type: BaseType, Log2LMUL, PrototypeDescriptors: OverloadedSuffixProto);
303
304 // Create non-masked intrinsic.
305 InitRVVIntrinsic(Record, SuffixStr, OverloadedSuffixStr, IsMask: false, Types&: *Types,
306 HasPolicy: UnMaskedHasPolicy, PolicyAttrs: DefaultPolicy);
307
308 // Create non-masked policy intrinsic.
309 if (Record.UnMaskedPolicyScheme != PolicyScheme::SchemeNone) {
310 for (auto P : SupportedUnMaskedPolicies) {
311 llvm::SmallVector<PrototypeDescriptor> PolicyPrototype =
312 RVVIntrinsic::computeBuiltinTypes(
313 Prototype: BasicProtoSeq, /*IsMasked=*/false,
314 /*HasMaskedOffOperand=*/false, HasVL: Record.HasVL, NF: Record.NF,
315 DefaultScheme: UnMaskedPolicyScheme, PolicyAttrs: P, IsTuple: Record.IsTuple);
316 std::optional<RVVTypes> PolicyTypes = TypeCache.computeTypes(
317 BT: BaseType, Log2LMUL, NF: Record.NF, Prototype: PolicyPrototype);
318 InitRVVIntrinsic(Record, SuffixStr, OverloadedSuffixStr,
319 /*IsMask=*/false, Types&: *PolicyTypes, HasPolicy: UnMaskedHasPolicy,
320 PolicyAttrs: P);
321 }
322 }
323 if (!Record.HasMasked)
324 continue;
325 // Create masked intrinsic.
326 std::optional<RVVTypes> MaskTypes =
327 TypeCache.computeTypes(BT: BaseType, Log2LMUL, NF: Record.NF, Prototype: ProtoMaskSeq);
328 InitRVVIntrinsic(Record, SuffixStr, OverloadedSuffixStr, IsMask: true,
329 Types&: *MaskTypes, HasPolicy: MaskedHasPolicy, PolicyAttrs: DefaultPolicy);
330 if (Record.MaskedPolicyScheme == PolicyScheme::SchemeNone)
331 continue;
332 // Create masked policy intrinsic.
333 for (auto P : SupportedMaskedPolicies) {
334 llvm::SmallVector<PrototypeDescriptor> PolicyPrototype =
335 RVVIntrinsic::computeBuiltinTypes(
336 Prototype: BasicProtoSeq, /*IsMasked=*/true, HasMaskedOffOperand: Record.HasMaskedOffOperand,
337 HasVL: Record.HasVL, NF: Record.NF, DefaultScheme: MaskedPolicyScheme, PolicyAttrs: P,
338 IsTuple: Record.IsTuple);
339 std::optional<RVVTypes> PolicyTypes = TypeCache.computeTypes(
340 BT: BaseType, Log2LMUL, NF: Record.NF, Prototype: PolicyPrototype);
341 InitRVVIntrinsic(Record, SuffixStr, OverloadedSuffixStr,
342 /*IsMask=*/true, Types&: *PolicyTypes, HasPolicy: MaskedHasPolicy, PolicyAttrs: P);
343 }
344 } // End for different LMUL
345 } // End for different TypeRange
346 }
347}
348
349void RISCVIntrinsicManagerImpl::InitIntrinsicList() {
350
351 if (S.DeclareRISCVVBuiltins && !ConstructedRISCVVBuiltins) {
352 ConstructedRISCVVBuiltins = true;
353 ConstructRVVIntrinsics(RVVIntrinsicRecords,
354 IntrinsicKind::RVV);
355 }
356 if (S.DeclareRISCVSiFiveVectorBuiltins &&
357 !ConstructedRISCVSiFiveVectorBuiltins) {
358 ConstructedRISCVSiFiveVectorBuiltins = true;
359 ConstructRVVIntrinsics(RVSiFiveVectorIntrinsicRecords,
360 IntrinsicKind::SIFIVE_VECTOR);
361 }
362}
363
364// Compute name and signatures for intrinsic with practical types.
365void RISCVIntrinsicManagerImpl::InitRVVIntrinsic(
366 const RVVIntrinsicRecord &Record, StringRef SuffixStr,
367 StringRef OverloadedSuffixStr, bool IsMasked, RVVTypes &Signature,
368 bool HasPolicy, Policy PolicyAttrs) {
369 // Function name, e.g. vadd_vv_i32m1.
370 std::string Name = Record.Name;
371 if (!SuffixStr.empty())
372 Name += "_" + SuffixStr.str();
373
374 // Overloaded function name, e.g. vadd.
375 std::string OverloadedName;
376 if (!Record.OverloadedName)
377 OverloadedName = StringRef(Record.Name).split(Separator: "_").first.str();
378 else
379 OverloadedName = Record.OverloadedName;
380 if (!OverloadedSuffixStr.empty())
381 OverloadedName += "_" + OverloadedSuffixStr.str();
382
383 // clang built-in function name, e.g. __builtin_rvv_vadd.
384 std::string BuiltinName = std::string(Record.Name);
385
386 RVVIntrinsic::updateNamesAndPolicy(IsMasked, HasPolicy, Name, BuiltinName,
387 OverloadedName, PolicyAttrs,
388 HasFRMRoundModeOp: Record.HasFRMRoundModeOp);
389
390 // Put into IntrinsicList.
391 uint16_t Index = IntrinsicList.size();
392 assert(IntrinsicList.size() == (size_t)Index &&
393 "Intrinsics indices overflow.");
394 IntrinsicList.push_back(x: {.BuiltinName: BuiltinName, .Signature: Signature});
395
396 // Creating mapping to Intrinsics.
397 Intrinsics.insert(KV: {Name, Index});
398
399 // Get the RVVOverloadIntrinsicDef.
400 RVVOverloadIntrinsicDef &OverloadIntrinsicDef =
401 OverloadIntrinsics[OverloadedName];
402
403 // And added the index.
404 OverloadIntrinsicDef.Indexes.push_back(Elt: Index);
405}
406
407void RISCVIntrinsicManagerImpl::CreateRVVIntrinsicDecl(LookupResult &LR,
408 IdentifierInfo *II,
409 Preprocessor &PP,
410 uint32_t Index,
411 bool IsOverload) {
412 ASTContext &Context = S.Context;
413 RVVIntrinsicDef &IDef = IntrinsicList[Index];
414 RVVTypes Sigs = IDef.Signature;
415 size_t SigLength = Sigs.size();
416 RVVType *ReturnType = Sigs[0];
417 QualType RetType = RVVType2Qual(Context, Type: ReturnType);
418 SmallVector<QualType, 8> ArgTypes;
419 QualType BuiltinFuncType;
420
421 // Skip return type, and convert RVVType to QualType for arguments.
422 for (size_t i = 1; i < SigLength; ++i)
423 ArgTypes.push_back(Elt: RVVType2Qual(Context, Type: Sigs[i]));
424
425 FunctionProtoType::ExtProtoInfo PI(
426 Context.getDefaultCallingConvention(IsVariadic: false, IsCXXMethod: false, IsBuiltin: true));
427
428 PI.Variadic = false;
429
430 SourceLocation Loc = LR.getNameLoc();
431 BuiltinFuncType = Context.getFunctionType(ResultTy: RetType, Args: ArgTypes, EPI: PI);
432 DeclContext *Parent = Context.getTranslationUnitDecl();
433
434 FunctionDecl *RVVIntrinsicDecl = FunctionDecl::Create(
435 C&: Context, DC: Parent, StartLoc: Loc, NLoc: Loc, N: II, T: BuiltinFuncType, /*TInfo=*/nullptr,
436 SC: SC_Extern, UsesFPIntrin: S.getCurFPFeatures().isFPConstrained(),
437 /*isInlineSpecified*/ false,
438 /*hasWrittenPrototype*/ true);
439
440 // Create Decl objects for each parameter, adding them to the
441 // FunctionDecl.
442 const auto *FP = cast<FunctionProtoType>(Val&: BuiltinFuncType);
443 SmallVector<ParmVarDecl *, 8> ParmList;
444 for (unsigned IParm = 0, E = FP->getNumParams(); IParm != E; ++IParm) {
445 ParmVarDecl *Parm =
446 ParmVarDecl::Create(Context, RVVIntrinsicDecl, Loc, Loc, nullptr,
447 FP->getParamType(i: IParm), nullptr, SC_None, nullptr);
448 Parm->setScopeInfo(scopeDepth: 0, parameterIndex: IParm);
449 ParmList.push_back(Elt: Parm);
450 }
451 RVVIntrinsicDecl->setParams(ParmList);
452
453 // Add function attributes.
454 if (IsOverload)
455 RVVIntrinsicDecl->addAttr(OverloadableAttr::CreateImplicit(Context));
456
457 // Setup alias to __builtin_rvv_*
458 IdentifierInfo &IntrinsicII =
459 PP.getIdentifierTable().get(Name: "__builtin_rvv_" + IDef.BuiltinName);
460 RVVIntrinsicDecl->addAttr(
461 BuiltinAliasAttr::CreateImplicit(S.Context, &IntrinsicII));
462
463 // Add to symbol table.
464 LR.addDecl(RVVIntrinsicDecl);
465}
466
467bool RISCVIntrinsicManagerImpl::CreateIntrinsicIfFound(LookupResult &LR,
468 IdentifierInfo *II,
469 Preprocessor &PP) {
470 StringRef Name = II->getName();
471 if (!Name.consume_front(Prefix: "__riscv_"))
472 return false;
473
474 // Lookup the function name from the overload intrinsics first.
475 auto OvIItr = OverloadIntrinsics.find(Key: Name);
476 if (OvIItr != OverloadIntrinsics.end()) {
477 const RVVOverloadIntrinsicDef &OvIntrinsicDef = OvIItr->second;
478 for (auto Index : OvIntrinsicDef.Indexes)
479 CreateRVVIntrinsicDecl(LR, II, PP, Index,
480 /*IsOverload*/ true);
481
482 // If we added overloads, need to resolve the lookup result.
483 LR.resolveKind();
484 return true;
485 }
486
487 // Lookup the function name from the intrinsics.
488 auto Itr = Intrinsics.find(Key: Name);
489 if (Itr != Intrinsics.end()) {
490 CreateRVVIntrinsicDecl(LR, II, PP, Index: Itr->second,
491 /*IsOverload*/ false);
492 return true;
493 }
494
495 // It's not an RVV intrinsics.
496 return false;
497}
498
499namespace clang {
500std::unique_ptr<clang::sema::RISCVIntrinsicManager>
501CreateRISCVIntrinsicManager(Sema &S) {
502 return std::make_unique<RISCVIntrinsicManagerImpl>(args&: S);
503}
504} // namespace clang
505

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