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