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