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<uint32_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(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
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<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
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      {"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
347void 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.
363void 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
403void 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
462bool 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
492namespace clang {
493std::unique_ptr<clang::sema::RISCVIntrinsicManager>
494CreateRISCVIntrinsicManager(Sema &S) {
495  return std::make_unique<RISCVIntrinsicManagerImpl>(S);
496}
497} // namespace clang
498