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