1 //==- SemaRISCVVectorLookup.cpp - Name Lookup for RISC-V Vector Intrinsic -==//
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
7 //===----------------------------------------------------------------------===//
9 // This file implements name lookup for RISC-V vector intrinsic.
11 //===----------------------------------------------------------------------===//
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"
28 using namespace clang
;
29 using namespace clang::RISCV
;
31 using IntrinsicKind
= sema::RISCVIntrinsicManager::IntrinsicKind
;
35 // Function definition of a RVV intrinsic.
36 struct RVVIntrinsicDef
{
37 /// Full function name with suffix, e.g. vadd_vv_i32m1.
40 /// Overloaded function name, e.g. vadd.
41 std::string OverloadName
;
43 /// Mapping to which clang built-in function, e.g. __builtin_rvv_vadd.
44 std::string BuiltinName
;
46 /// Function signature, first element is return type.
50 struct RVVOverloadIntrinsicDef
{
51 // Indexes of RISCVIntrinsicManagerImpl::IntrinsicList.
52 SmallVector
<size_t, 8> Indexes
;
57 static const PrototypeDescriptor RVVSignatureTable
[] = {
58 #define DECL_SIGNATURE_TABLE
59 #include "clang/Basic/riscv_vector_builtin_sema.inc"
60 #undef DECL_SIGNATURE_TABLE
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
69 static const RVVIntrinsicRecord RVVIntrinsicRecords
[] = {
70 #define DECL_INTRINSIC_RECORDS
71 #include "clang/Basic/riscv_vector_builtin_sema.inc"
72 #undef DECL_INTRINSIC_RECORDS
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
81 // Get subsequence of signature table.
82 static ArrayRef
<PrototypeDescriptor
>
83 ProtoSeq2ArrayRef(IntrinsicKind K
, uint16_t Index
, uint8_t Length
) {
85 case IntrinsicKind::RVV
:
86 return ArrayRef(&RVVSignatureTable
[Index
], Length
);
87 case IntrinsicKind::SIFIVE_VECTOR
:
88 return ArrayRef(&RVSiFiveVectorSignatureTable
[Index
], Length
);
90 llvm_unreachable("Unhandled IntrinsicKind");
93 static QualType
RVVType2Qual(ASTContext
&Context
, const RVVType
*Type
) {
95 switch (Type
->getScalarType()) {
96 case ScalarTypeKind::Void
:
99 case ScalarTypeKind::Size_t
:
100 QT
= Context
.getSizeType();
102 case ScalarTypeKind::Ptrdiff_t
:
103 QT
= Context
.getPointerDiffType();
105 case ScalarTypeKind::UnsignedLong
:
106 QT
= Context
.UnsignedLongTy
;
108 case ScalarTypeKind::SignedLong
:
111 case ScalarTypeKind::Boolean
:
114 case ScalarTypeKind::SignedInteger
:
115 QT
= Context
.getIntTypeForBitwidth(Type
->getElementBitwidth(), true);
117 case ScalarTypeKind::UnsignedInteger
:
118 QT
= Context
.getIntTypeForBitwidth(Type
->getElementBitwidth(), false);
120 case ScalarTypeKind::Float
:
121 switch (Type
->getElementBitwidth()) {
123 QT
= Context
.DoubleTy
;
126 QT
= Context
.FloatTy
;
129 QT
= Context
.Float16Ty
;
132 llvm_unreachable("Unsupported floating point width.");
137 llvm_unreachable("Unhandled type.");
139 if (Type
->isVector()) {
141 QT
= Context
.getScalableVectorType(QT
, *Type
->getScale(), Type
->getNF());
143 QT
= Context
.getScalableVectorType(QT
, *Type
->getScale());
146 if (Type
->isConstant())
147 QT
= Context
.getConstType(QT
);
149 // Transform the type to a pointer as the last step, if necessary.
150 if (Type
->isPointer())
151 QT
= Context
.getPointerType(QT
);
157 class RISCVIntrinsicManagerImpl
: public sema::RISCVIntrinsicManager
{
161 RVVTypeCache TypeCache
;
162 bool ConstructedRISCVVBuiltins
;
163 bool ConstructedRISCVSiFiveVectorBuiltins
;
165 // List of all RVV intrinsic.
166 std::vector
<RVVIntrinsicDef
> IntrinsicList
;
167 // Mapping function name to index of IntrinsicList.
168 StringMap
<size_t> Intrinsics
;
169 // Mapping function name to RVVOverloadIntrinsicDef.
170 StringMap
<RVVOverloadIntrinsicDef
> OverloadIntrinsics
;
173 // Create RVVIntrinsicDef.
174 void InitRVVIntrinsic(const RVVIntrinsicRecord
&Record
, StringRef SuffixStr
,
175 StringRef OverloadedSuffixStr
, bool IsMask
,
176 RVVTypes
&Types
, bool HasPolicy
, Policy PolicyAttrs
);
178 // Create FunctionDecl for a vector intrinsic.
179 void CreateRVVIntrinsicDecl(LookupResult
&LR
, IdentifierInfo
*II
,
180 Preprocessor
&PP
, unsigned Index
,
183 void ConstructRVVIntrinsics(ArrayRef
<RVVIntrinsicRecord
> Recs
,
187 RISCVIntrinsicManagerImpl(clang::Sema
&S
) : S(S
), Context(S
.Context
) {
188 ConstructedRISCVVBuiltins
= false;
189 ConstructedRISCVSiFiveVectorBuiltins
= false;
192 // Initialize IntrinsicList
193 void InitIntrinsicList() override
;
195 // Create RISC-V vector intrinsic and insert into symbol table if found, and
196 // return true, otherwise return false.
197 bool CreateIntrinsicIfFound(LookupResult
&LR
, IdentifierInfo
*II
,
198 Preprocessor
&PP
) override
;
202 void RISCVIntrinsicManagerImpl::ConstructRVVIntrinsics(
203 ArrayRef
<RVVIntrinsicRecord
> Recs
, IntrinsicKind K
) {
204 const TargetInfo
&TI
= Context
.getTargetInfo();
205 static const std::pair
<const char *, RVVRequire
> FeatureCheckList
[] = {
206 {"64bit", RVV_REQ_RV64
},
207 {"xsfvcp", RVV_REQ_Xsfvcp
},
208 {"xsfvfnrclipxfqf", RVV_REQ_Xsfvfnrclipxfqf
},
209 {"xsfvfwmaccqqq", RVV_REQ_Xsfvfwmaccqqq
},
210 {"xsfvqmaccdod", RVV_REQ_Xsfvqmaccdod
},
211 {"xsfvqmaccqoq", RVV_REQ_Xsfvqmaccqoq
},
212 {"experimental-zvbb", RVV_REQ_Zvbb
},
213 {"experimental-zvbc", RVV_REQ_Zvbc
},
214 {"experimental-zvkb", RVV_REQ_Zvkb
},
215 {"experimental-zvkg", RVV_REQ_Zvkg
},
216 {"experimental-zvkned", RVV_REQ_Zvkned
},
217 {"experimental-zvknha", RVV_REQ_Zvknha
},
218 {"experimental-zvknhb", RVV_REQ_Zvknhb
},
219 {"experimental-zvksed", RVV_REQ_Zvksed
},
220 {"experimental-zvksh", RVV_REQ_Zvksh
}};
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(FeatureCheckList
, [&](const auto &Item
) {
227 return (Record
.RequiredExtensions
& Item
.second
) == Item
.second
&&
228 !TI
.hasFeature(Item
.first
);
232 // Create Intrinsics for each type and LMUL.
233 BasicType BaseType
= BasicType::Unknown
;
234 ArrayRef
<PrototypeDescriptor
> BasicProtoSeq
=
235 ProtoSeq2ArrayRef(K
, Record
.PrototypeIndex
, Record
.PrototypeLength
);
236 ArrayRef
<PrototypeDescriptor
> SuffixProto
=
237 ProtoSeq2ArrayRef(K
, Record
.SuffixIndex
, Record
.SuffixLength
);
238 ArrayRef
<PrototypeDescriptor
> OverloadedSuffixProto
= ProtoSeq2ArrayRef(
239 K
, Record
.OverloadedSuffixIndex
, Record
.OverloadedSuffixSize
);
241 PolicyScheme UnMaskedPolicyScheme
=
242 static_cast<PolicyScheme
>(Record
.UnMaskedPolicyScheme
);
243 PolicyScheme MaskedPolicyScheme
=
244 static_cast<PolicyScheme
>(Record
.MaskedPolicyScheme
);
246 const Policy DefaultPolicy
;
248 llvm::SmallVector
<PrototypeDescriptor
> ProtoSeq
=
249 RVVIntrinsic::computeBuiltinTypes(
250 BasicProtoSeq
, /*IsMasked=*/false,
251 /*HasMaskedOffOperand=*/false, Record
.HasVL
, Record
.NF
,
252 UnMaskedPolicyScheme
, DefaultPolicy
, Record
.IsTuple
);
254 llvm::SmallVector
<PrototypeDescriptor
> ProtoMaskSeq
;
255 if (Record
.HasMasked
)
256 ProtoMaskSeq
= RVVIntrinsic::computeBuiltinTypes(
257 BasicProtoSeq
, /*IsMasked=*/true, Record
.HasMaskedOffOperand
,
258 Record
.HasVL
, Record
.NF
, MaskedPolicyScheme
, DefaultPolicy
,
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(Record
.HasTailPolicy
,
267 Record
.HasMaskPolicy
);
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
);
275 if ((BaseTypeI
& Record
.TypeRangeMask
) != BaseTypeI
)
278 if (BaseType
== BasicType::Float16
) {
279 if ((Record
.RequiredExtensions
& RVV_REQ_ZvfhminOrZvfh
) ==
280 RVV_REQ_ZvfhminOrZvfh
) {
281 if (!TI
.hasFeature("zvfh") && !TI
.hasFeature("zvfhmin"))
283 } else if (!TI
.hasFeature("zvfh")) {
288 // Expanded with different LMUL.
289 for (int Log2LMUL
= -3; Log2LMUL
<= 3; Log2LMUL
++) {
290 if (!(Record
.Log2LMULMask
& (1 << (Log2LMUL
+ 3))))
293 std::optional
<RVVTypes
> Types
=
294 TypeCache
.computeTypes(BaseType
, Log2LMUL
, Record
.NF
, ProtoSeq
);
296 // Ignored to create new intrinsic if there are any illegal types.
297 if (!Types
.has_value())
300 std::string SuffixStr
= RVVIntrinsic::getSuffixStr(
301 TypeCache
, BaseType
, Log2LMUL
, SuffixProto
);
302 std::string OverloadedSuffixStr
= RVVIntrinsic::getSuffixStr(
303 TypeCache
, BaseType
, Log2LMUL
, OverloadedSuffixProto
);
305 // Create non-masked intrinsic.
306 InitRVVIntrinsic(Record
, SuffixStr
, OverloadedSuffixStr
, false, *Types
,
307 UnMaskedHasPolicy
, DefaultPolicy
);
309 // Create non-masked policy intrinsic.
310 if (Record
.UnMaskedPolicyScheme
!= PolicyScheme::SchemeNone
) {
311 for (auto P
: SupportedUnMaskedPolicies
) {
312 llvm::SmallVector
<PrototypeDescriptor
> PolicyPrototype
=
313 RVVIntrinsic::computeBuiltinTypes(
314 BasicProtoSeq
, /*IsMasked=*/false,
315 /*HasMaskedOffOperand=*/false, Record
.HasVL
, Record
.NF
,
316 UnMaskedPolicyScheme
, P
, Record
.IsTuple
);
317 std::optional
<RVVTypes
> PolicyTypes
= TypeCache
.computeTypes(
318 BaseType
, Log2LMUL
, Record
.NF
, PolicyPrototype
);
319 InitRVVIntrinsic(Record
, SuffixStr
, OverloadedSuffixStr
,
320 /*IsMask=*/false, *PolicyTypes
, UnMaskedHasPolicy
,
324 if (!Record
.HasMasked
)
326 // Create masked intrinsic.
327 std::optional
<RVVTypes
> MaskTypes
=
328 TypeCache
.computeTypes(BaseType
, Log2LMUL
, Record
.NF
, ProtoMaskSeq
);
329 InitRVVIntrinsic(Record
, SuffixStr
, OverloadedSuffixStr
, true,
330 *MaskTypes
, MaskedHasPolicy
, DefaultPolicy
);
331 if (Record
.MaskedPolicyScheme
== PolicyScheme::SchemeNone
)
333 // Create masked policy intrinsic.
334 for (auto P
: SupportedMaskedPolicies
) {
335 llvm::SmallVector
<PrototypeDescriptor
> PolicyPrototype
=
336 RVVIntrinsic::computeBuiltinTypes(
337 BasicProtoSeq
, /*IsMasked=*/true, Record
.HasMaskedOffOperand
,
338 Record
.HasVL
, Record
.NF
, MaskedPolicyScheme
, P
,
340 std::optional
<RVVTypes
> PolicyTypes
= TypeCache
.computeTypes(
341 BaseType
, Log2LMUL
, Record
.NF
, PolicyPrototype
);
342 InitRVVIntrinsic(Record
, SuffixStr
, OverloadedSuffixStr
,
343 /*IsMask=*/true, *PolicyTypes
, MaskedHasPolicy
, P
);
345 } // End for different LMUL
346 } // End for different TypeRange
350 void RISCVIntrinsicManagerImpl::InitIntrinsicList() {
352 if (S
.DeclareRISCVVBuiltins
&& !ConstructedRISCVVBuiltins
) {
353 ConstructedRISCVVBuiltins
= true;
354 ConstructRVVIntrinsics(RVVIntrinsicRecords
,
357 if (S
.DeclareRISCVSiFiveVectorBuiltins
&&
358 !ConstructedRISCVSiFiveVectorBuiltins
) {
359 ConstructedRISCVSiFiveVectorBuiltins
= true;
360 ConstructRVVIntrinsics(RVSiFiveVectorIntrinsicRecords
,
361 IntrinsicKind::SIFIVE_VECTOR
);
365 // Compute name and signatures for intrinsic with practical types.
366 void RISCVIntrinsicManagerImpl::InitRVVIntrinsic(
367 const RVVIntrinsicRecord
&Record
, StringRef SuffixStr
,
368 StringRef OverloadedSuffixStr
, bool IsMasked
, RVVTypes
&Signature
,
369 bool HasPolicy
, Policy PolicyAttrs
) {
370 // Function name, e.g. vadd_vv_i32m1.
371 std::string Name
= Record
.Name
;
372 if (!SuffixStr
.empty())
373 Name
+= "_" + SuffixStr
.str();
375 // Overloaded function name, e.g. vadd.
376 std::string OverloadedName
;
377 if (!Record
.OverloadedName
)
378 OverloadedName
= StringRef(Record
.Name
).split("_").first
.str();
380 OverloadedName
= Record
.OverloadedName
;
381 if (!OverloadedSuffixStr
.empty())
382 OverloadedName
+= "_" + OverloadedSuffixStr
.str();
384 // clang built-in function name, e.g. __builtin_rvv_vadd.
385 std::string BuiltinName
= "__builtin_rvv_" + std::string(Record
.Name
);
387 RVVIntrinsic::updateNamesAndPolicy(IsMasked
, HasPolicy
, Name
, BuiltinName
,
388 OverloadedName
, PolicyAttrs
,
389 Record
.HasFRMRoundModeOp
);
391 // Put into IntrinsicList.
392 size_t Index
= IntrinsicList
.size();
393 IntrinsicList
.push_back({Name
, OverloadedName
, BuiltinName
, Signature
});
395 // Creating mapping to Intrinsics.
396 Intrinsics
.insert({Name
, Index
});
398 // Get the RVVOverloadIntrinsicDef.
399 RVVOverloadIntrinsicDef
&OverloadIntrinsicDef
=
400 OverloadIntrinsics
[OverloadedName
];
402 // And added the index.
403 OverloadIntrinsicDef
.Indexes
.push_back(Index
);
406 void RISCVIntrinsicManagerImpl::CreateRVVIntrinsicDecl(LookupResult
&LR
,
411 ASTContext
&Context
= S
.Context
;
412 RVVIntrinsicDef
&IDef
= IntrinsicList
[Index
];
413 RVVTypes Sigs
= IDef
.Signature
;
414 size_t SigLength
= Sigs
.size();
415 RVVType
*ReturnType
= Sigs
[0];
416 QualType RetType
= RVVType2Qual(Context
, ReturnType
);
417 SmallVector
<QualType
, 8> ArgTypes
;
418 QualType BuiltinFuncType
;
420 // Skip return type, and convert RVVType to QualType for arguments.
421 for (size_t i
= 1; i
< SigLength
; ++i
)
422 ArgTypes
.push_back(RVVType2Qual(Context
, Sigs
[i
]));
424 FunctionProtoType::ExtProtoInfo
PI(
425 Context
.getDefaultCallingConvention(false, false, true));
429 SourceLocation Loc
= LR
.getNameLoc();
430 BuiltinFuncType
= Context
.getFunctionType(RetType
, ArgTypes
, PI
);
431 DeclContext
*Parent
= Context
.getTranslationUnitDecl();
433 FunctionDecl
*RVVIntrinsicDecl
= FunctionDecl::Create(
434 Context
, Parent
, Loc
, Loc
, II
, BuiltinFuncType
, /*TInfo=*/nullptr,
435 SC_Extern
, S
.getCurFPFeatures().isFPConstrained(),
436 /*isInlineSpecified*/ false,
437 /*hasWrittenPrototype*/ true);
439 // Create Decl objects for each parameter, adding them to the
441 const auto *FP
= cast
<FunctionProtoType
>(BuiltinFuncType
);
442 SmallVector
<ParmVarDecl
*, 8> ParmList
;
443 for (unsigned IParm
= 0, E
= FP
->getNumParams(); IParm
!= E
; ++IParm
) {
445 ParmVarDecl::Create(Context
, RVVIntrinsicDecl
, Loc
, Loc
, nullptr,
446 FP
->getParamType(IParm
), nullptr, SC_None
, nullptr);
447 Parm
->setScopeInfo(0, IParm
);
448 ParmList
.push_back(Parm
);
450 RVVIntrinsicDecl
->setParams(ParmList
);
452 // Add function attributes.
454 RVVIntrinsicDecl
->addAttr(OverloadableAttr::CreateImplicit(Context
));
456 // Setup alias to __builtin_rvv_*
457 IdentifierInfo
&IntrinsicII
= PP
.getIdentifierTable().get(IDef
.BuiltinName
);
458 RVVIntrinsicDecl
->addAttr(
459 BuiltinAliasAttr::CreateImplicit(S
.Context
, &IntrinsicII
));
461 // Add to symbol table.
462 LR
.addDecl(RVVIntrinsicDecl
);
465 bool RISCVIntrinsicManagerImpl::CreateIntrinsicIfFound(LookupResult
&LR
,
468 StringRef Name
= II
->getName();
470 // Lookup the function name from the overload intrinsics first.
471 auto OvIItr
= OverloadIntrinsics
.find(Name
);
472 if (OvIItr
!= OverloadIntrinsics
.end()) {
473 const RVVOverloadIntrinsicDef
&OvIntrinsicDef
= OvIItr
->second
;
474 for (auto Index
: OvIntrinsicDef
.Indexes
)
475 CreateRVVIntrinsicDecl(LR
, II
, PP
, Index
,
476 /*IsOverload*/ true);
478 // If we added overloads, need to resolve the lookup result.
483 // Lookup the function name from the intrinsics.
484 auto Itr
= Intrinsics
.find(Name
);
485 if (Itr
!= Intrinsics
.end()) {
486 CreateRVVIntrinsicDecl(LR
, II
, PP
, Itr
->second
,
487 /*IsOverload*/ false);
491 // It's not an RVV intrinsics.
496 std::unique_ptr
<clang::sema::RISCVIntrinsicManager
>
497 CreateRISCVIntrinsicManager(Sema
&S
) {
498 return std::make_unique
<RISCVIntrinsicManagerImpl
>(S
);